Docker config
This commit is contained in:
95
app/components/About/NGSSection.jsx
Normal file
95
app/components/About/NGSSection.jsx
Normal file
@ -0,0 +1,95 @@
|
||||
import React from "react";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
|
||||
const AdvantageCard = ({ icon, title, description }) => (
|
||||
<div className="p-6 rounded-2xl shadow-sm border-0 h-full transition-transform duration-300 ease-in-out hover:scale-105 hover:shadow-lg group" style={{ backgroundColor: '#f2fcfc' }}>
|
||||
<div className="flex items-center space-x-4 mb-4">
|
||||
<div className="w-12 h-12 bg-orange-100 rounded-xl flex items-center justify-center flex-shrink-0">
|
||||
<Image src={icon} alt={title} width={24} height={24} />
|
||||
</div>
|
||||
<h3 className="text-lg font-semibold text-teal-700 leading-tight">{title}</h3>
|
||||
</div>
|
||||
<div className="relative w-full h-px bg-gray-300 mb-4 overflow-hidden">
|
||||
<div className="absolute top-0 left-0 h-full bg-gray-600 w-0 group-hover:w-full transition-all duration-500 ease-in-out"></div>
|
||||
</div>
|
||||
<p className="text-gray-600 leading-relaxed text-sm text-justify">{description}</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
const NGSSection = () => {
|
||||
const advantages = [
|
||||
{
|
||||
icon: "/images/homepage-1/service/Advantages-NGS-Icons-20.svg",
|
||||
title: "High Throughput",
|
||||
description: "NGS technology has the capacity to generate an abundance of sequence data within a relatively short timespan."
|
||||
},
|
||||
{
|
||||
icon: "/images/homepage-1/service/Advantages-NGS-Icons-21.svg",
|
||||
title: "High Resolution",
|
||||
description: "Advanced sequencing platforms provide highly accurate DNA/RNA data, detecting even minor variations. This precision drives breakthroughs in health, diagnostics, agriculture, and molecular biology."
|
||||
},
|
||||
{
|
||||
icon: "/images/homepage-1/service/Advantages-NGS-Icons-22.svg",
|
||||
title: "Cost Efficient",
|
||||
description: "Affordability of the technology facilitates researchers in the successful implementation of large-scale sequencing projects."
|
||||
},
|
||||
{
|
||||
icon: "/images/homepage-1/service/Advantages-NGS-Icons-23.svg",
|
||||
title: "Flexible",
|
||||
description: "Supports a wide range of sample types and library construction methods, making it suitable for various research objectives and experimental designs."
|
||||
},
|
||||
{
|
||||
icon: "/images/homepage-1/service/Advantages-NGS-Icons-24.svg",
|
||||
title: "Time Effective",
|
||||
description: "Rapid sequencing of large genetic material be completed within a comparatively short duration, thereby yielding quick results."
|
||||
},
|
||||
{
|
||||
icon: "/images/homepage-1/service/Advantages-NGS-Icons-24.svg",
|
||||
title: "Bioinformatics Analysis",
|
||||
description: "NGS produces vast amounts of data, supporting complex research through advanced bioinformatic analysis."
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<section className="py-10 bg-white">
|
||||
<div className="px-6">
|
||||
{/* NGS Introduction */}
|
||||
<div>
|
||||
<h2 className="text-4xl font-bold text-teal-700 mb-6 leading-tight">
|
||||
NGS and Its Advantages
|
||||
</h2>
|
||||
<p className="text-gray-600 text-base leading-relaxed mb-8 text-justify">
|
||||
Next Generation Sequencing (NGS) is a transformative technology
|
||||
that has revolutionized scientific research. Unlike traditional
|
||||
methods that required years to decode a single genome, NGS can
|
||||
sequence entire genomes within days. This groundbreaking
|
||||
technology employs massively parallel sequencing, simultaneously
|
||||
analyzing millions of small DNA fragments, allowing researchers to
|
||||
investigate thousands of genomic regions at single-base resolution
|
||||
in a single experiment, empowering them to tackle ambitious
|
||||
questions and achieve comprehensive insights.
|
||||
</p>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
{/* Advantages Grid */}
|
||||
<div className="mt-6">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
{advantages.map((advantage, index) => (
|
||||
<AdvantageCard
|
||||
key={index}
|
||||
icon={advantage.icon}
|
||||
title={advantage.title}
|
||||
description={advantage.description}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default NGSSection;
|
||||
75
app/components/Advantages/AdvantagesSection.jsx
Normal file
75
app/components/Advantages/AdvantagesSection.jsx
Normal file
@ -0,0 +1,75 @@
|
||||
import React from 'react';
|
||||
import Image from 'next/image';
|
||||
|
||||
const AdvantageCard = ({ icon, title, description }) => (
|
||||
<div className="p-6 rounded-2xl shadow-sm border-0 h-full transition-transform duration-300 ease-in-out hover:scale-105 hover:shadow-lg group" style={{ backgroundColor: '#f2fcfc' }}>
|
||||
<div className="flex items-center space-x-4 mb-4">
|
||||
<div className="w-12 h-12 bg-orange-100 rounded-xl flex items-center justify-center flex-shrink-0">
|
||||
<Image src={icon} alt={title} width={24} height={24} />
|
||||
</div>
|
||||
<h3 className="text-lg font-semibold text-teal-700 leading-tight">{title}</h3>
|
||||
</div>
|
||||
<div className="relative w-full h-px bg-gray-300 mb-4 overflow-hidden">
|
||||
<div className="absolute top-0 left-0 h-full bg-gray-600 w-0 group-hover:w-full transition-all duration-500 ease-in-out"></div>
|
||||
</div>
|
||||
<p className="text-gray-600 leading-relaxed text-sm text-justify">{description}</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
const AdvantagesSection = () => {
|
||||
const advantages = [
|
||||
{
|
||||
icon: "/images/homepage-1/service/Advantages-NGS-Icons-20.svg",
|
||||
title: "High Throughput",
|
||||
description: "NGS technology has the capacity to generate an abundance of sequence data within a relatively short timespan."
|
||||
},
|
||||
{
|
||||
icon: "/images/homepage-1/service/Advantages-NGS-Icons-21.svg",
|
||||
title: "High Resolution",
|
||||
description: "Advanced sequencing platforms provide highly accurate DNA/RNA data, detecting even minor variations. This precision drives breakthroughs in health, diagnostics, agriculture, and molecular biology."
|
||||
},
|
||||
{
|
||||
icon: "/images/homepage-1/service/Advantages-NGS-Icons-22.svg",
|
||||
title: "Cost Efficient",
|
||||
description: "Affordability of the technology facilitates researchers in the successful implementation of large-scale sequencing projects."
|
||||
},
|
||||
{
|
||||
icon: "/images/homepage-1/service/Advantages-NGS-Icons-23.svg",
|
||||
title: "Flexible",
|
||||
description: "Supports a wide range of sample types and library construction methods, making it suitable for various research objectives and experimental designs."
|
||||
},
|
||||
{
|
||||
icon: "/images/homepage-1/service/Advantages-NGS-Icons-24.svg",
|
||||
title: "Time Effective",
|
||||
description: "Rapid sequencing of large genetic material be completed within a comparatively short duration, thereby yielding quick results."
|
||||
},
|
||||
{
|
||||
icon: "/images/homepage-1/service/Advantages-NGS-Icons-24.svg",
|
||||
title: "Bioinformatics Analysis",
|
||||
description: "NGS produces vast amounts of data, supporting complex research through advanced bioinformatic analysis."
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<section className="py-6 bg-white">
|
||||
<div className="container mx-auto max-w-none px-6">
|
||||
<div className="mb-8">
|
||||
<h2 className="text-4xl font-bold text-teal-700 mb-4">Advantages of NGS</h2>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
{advantages.map((advantage, index) => (
|
||||
<AdvantageCard
|
||||
key={index}
|
||||
icon={advantage.icon}
|
||||
title={advantage.title}
|
||||
description={advantage.description}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default AdvantagesSection;
|
||||
43
app/components/CTA/CTASection.jsx
Normal file
43
app/components/CTA/CTASection.jsx
Normal file
@ -0,0 +1,43 @@
|
||||
import React from 'react';
|
||||
import Image from 'next/image';
|
||||
|
||||
const CTASection = () => {
|
||||
return (
|
||||
<section className="py-6">
|
||||
<div className="container mx-auto max-w-none px-6">
|
||||
<div className="rounded-3xl p-8 lg:p-12 text-center" style={{ backgroundColor: '#2a6564' }}>
|
||||
<h2 className="text-3xl lg:text-4xl font-bold text-white mb-8">
|
||||
Feeling Overwhelmed About Designing Your Experiment?
|
||||
</h2>
|
||||
|
||||
<div className="mb-8">
|
||||
<Image
|
||||
src="/images/homepage-2/Scientist.png"
|
||||
alt="Scientist"
|
||||
width={1000}
|
||||
height={800}
|
||||
className="rounded-2xl mx-auto max-w-full h-auto"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<h3 className="text-2xl lg:text-3xl font-bold text-yellow-400 mb-6">
|
||||
Operify Has You Covered
|
||||
</h3>
|
||||
|
||||
<p className="text-white text-base lg:text-xl leading-relaxed max-w-4xl mx-auto mb-8">
|
||||
We offer comprehensive assistance for planning your experiment, including design,
|
||||
execution, and evaluation of your sequencing project. Consult with our scientists
|
||||
and bioinformatics experts to tailor a workflow that suits your needs, process
|
||||
your samples, and generate your first NGS dataset.
|
||||
</p>
|
||||
|
||||
<button className="bg-yellow-400 text-white px-8 py-4 rounded-full text-lg font-semibold hover:bg-yellow-300 transition-colors">
|
||||
Enquire Now
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default CTASection;
|
||||
311
app/components/Career/CareerForm.jsx
Normal file
311
app/components/Career/CareerForm.jsx
Normal file
@ -0,0 +1,311 @@
|
||||
'use client';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
const ExperienceSelect = ({ value, onChange, required }) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
const experienceOptions = [
|
||||
{ value: '', label: 'Select Experience', disabled: true },
|
||||
{ value: 'Fresher', label: 'Fresher' },
|
||||
{ value: '1-2 years', label: '1-2 years' },
|
||||
{ value: '3-5 years', label: '3-5 years' },
|
||||
{ value: '5+ years', label: '5+ years' }
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
className="w-full h-[52px] px-5 bg-white border border-teal-400 rounded-full text-left focus:outline-none focus:ring-2 focus:ring-teal-500 focus:border-transparent appearance-none flex items-center justify-between"
|
||||
required={required}
|
||||
>
|
||||
<span className={value ? 'text-gray-800' : 'text-gray-500'}>
|
||||
{value || 'Select Experience'}
|
||||
</span>
|
||||
<svg
|
||||
className={`w-4 h-4 transition-transform ${isOpen ? 'rotate-180' : ''}`}
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
{isOpen && (
|
||||
<div className="absolute z-10 w-full mt-1 bg-white border border-gray-300 rounded-lg shadow-lg">
|
||||
{experienceOptions.map((option, index) => (
|
||||
<button
|
||||
key={index}
|
||||
type="button"
|
||||
disabled={option.disabled}
|
||||
onClick={() => {
|
||||
if (!option.disabled) {
|
||||
onChange(option.value);
|
||||
setIsOpen(false);
|
||||
}
|
||||
}}
|
||||
className={`w-full px-4 py-3 text-left hover:bg-gray-50 first:rounded-t-lg last:rounded-b-lg ${option.disabled ? 'text-gray-400 cursor-not-allowed' : 'text-gray-700'}`}
|
||||
>
|
||||
{option.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const FileUpload = ({ onFileChange, currentFile, required }) => {
|
||||
const handleFileSelect = (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (file) {
|
||||
onFileChange(file);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<input
|
||||
type="file"
|
||||
accept=".pdf,.doc,.docx"
|
||||
onChange={handleFileSelect}
|
||||
required={required}
|
||||
className="absolute inset-0 w-full h-full opacity-0 cursor-pointer"
|
||||
id="resume-upload"
|
||||
/>
|
||||
<div className="w-full h-[52px] px-5 bg-white border border-teal-400 rounded-full focus:outline-none focus:ring-2 focus:ring-teal-500 focus:border-transparent cursor-pointer flex items-center justify-between">
|
||||
<span className={currentFile ? 'text-gray-800' : 'text-gray-500'}>
|
||||
{currentFile ? currentFile.name : 'Upload Resume'}
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
className="bg-gray-100 text-gray-700 px-4 py-2 rounded-full text-sm hover:bg-gray-200 transition-colors flex-shrink-0"
|
||||
>
|
||||
Choose File
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const CareerForm = () => {
|
||||
const [formData, setFormData] = useState({
|
||||
name: '',
|
||||
phone: '',
|
||||
email: '',
|
||||
Education_Qualification: '',
|
||||
experience: '',
|
||||
Specify_your_interest_in_Genomics: '',
|
||||
message: '',
|
||||
resume: null
|
||||
});
|
||||
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const handleInputChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
[name]: value
|
||||
}));
|
||||
};
|
||||
|
||||
const handleSelectChange = (name, value) => {
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
[name]: value
|
||||
}));
|
||||
};
|
||||
|
||||
const handleFileChange = (file) => {
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
resume: file
|
||||
}));
|
||||
};
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
setIsSubmitting(true);
|
||||
|
||||
try {
|
||||
const formDataToSend = new FormData();
|
||||
Object.keys(formData).forEach(key => {
|
||||
if (formData[key]) {
|
||||
formDataToSend.append(key, formData[key]);
|
||||
}
|
||||
});
|
||||
formDataToSend.append('form_type', 'career');
|
||||
|
||||
console.log('Submitting career form:', formData);
|
||||
|
||||
const response = await fetch('/api/forms', {
|
||||
method: 'POST',
|
||||
body: formDataToSend,
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
console.log('API Response:', result);
|
||||
|
||||
if (response.ok) {
|
||||
alert(result.message);
|
||||
// Reset form
|
||||
setFormData({
|
||||
name: '',
|
||||
phone: '',
|
||||
email: '',
|
||||
Education_Qualification: '',
|
||||
experience: '',
|
||||
Specify_your_interest_in_Genomics: '',
|
||||
message: '',
|
||||
resume: null
|
||||
});
|
||||
} else {
|
||||
alert(result.error || 'Error submitting application. Please try again.');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error submitting form:', error);
|
||||
alert('Error submitting application. Please check your connection and try again.');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="lg:w-7/12">
|
||||
<div className="p-6 md:p-8 lg:p-10 rounded-3xl" style={{ backgroundColor: '#f2fcfc' }}>
|
||||
<div className="mb-8">
|
||||
<h2 className="text-2xl md:text-3xl font-normal text-teal-700 mb-2">
|
||||
Send a message
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-6">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<input
|
||||
type="text"
|
||||
name="name"
|
||||
value={formData.name}
|
||||
onChange={handleInputChange}
|
||||
placeholder="Your Name"
|
||||
required
|
||||
className="w-full px-5 py-4 bg-white border border-teal-400 rounded-full focus:outline-none focus:ring-2 focus:ring-teal-500 focus:border-transparent placeholder-gray-500"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
type="tel"
|
||||
name="phone"
|
||||
value={formData.phone}
|
||||
onChange={handleInputChange}
|
||||
placeholder="Your Phone"
|
||||
required
|
||||
className="w-full px-5 py-4 bg-white border border-teal-400 rounded-full focus:outline-none focus:ring-2 focus:ring-teal-500 focus:border-transparent placeholder-gray-500"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<input
|
||||
type="email"
|
||||
name="email"
|
||||
value={formData.email}
|
||||
onChange={handleInputChange}
|
||||
placeholder="Your Email"
|
||||
required
|
||||
className="w-full px-5 py-4 bg-white border border-teal-400 rounded-full focus:outline-none focus:ring-2 focus:ring-teal-500 focus:border-transparent placeholder-gray-500"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
type="text"
|
||||
name="Education_Qualification"
|
||||
value={formData.Education_Qualification}
|
||||
onChange={handleInputChange}
|
||||
placeholder="Education Qualification"
|
||||
required
|
||||
className="w-full px-5 py-4 bg-white border border-teal-400 rounded-full focus:outline-none focus:ring-2 focus:ring-teal-500 focus:border-transparent placeholder-gray-500"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<ExperienceSelect
|
||||
value={formData.experience}
|
||||
onChange={(value) => handleSelectChange('experience', value)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<FileUpload
|
||||
onFileChange={handleFileChange}
|
||||
currentFile={formData.resume}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<input
|
||||
type="text"
|
||||
name="Specify_your_interest_in_Genomics"
|
||||
value={formData.Specify_your_interest_in_Genomics}
|
||||
onChange={handleInputChange}
|
||||
placeholder="Specify your interest in Genomics"
|
||||
required
|
||||
className="w-full px-5 py-4 bg-white border border-teal-400 rounded-full focus:outline-none focus:ring-2 focus:ring-teal-500 focus:border-transparent placeholder-gray-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<textarea
|
||||
name="message"
|
||||
value={formData.message}
|
||||
onChange={handleInputChange}
|
||||
rows={3}
|
||||
placeholder="Any special Remarks"
|
||||
required
|
||||
className="w-full px-5 py-4 bg-white border border-teal-400 rounded-2xl focus:outline-none focus:ring-2 focus:ring-teal-500 focus:border-transparent resize-vertical placeholder-gray-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="pt-2 flex justify-end">
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isSubmitting}
|
||||
className="inline-flex items-center justify-center px-8 py-3 bg-teal-600 text-white font-medium rounded-full hover:bg-teal-700 transition-all duration-300 group disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
{isSubmitting ? (
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-current"></div>
|
||||
<span>Applying...</span>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<span className="mr-2">Apply</span>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
className="w-4 h-4 group-hover:translate-x-1 transition-transform duration-300"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth={2}
|
||||
>
|
||||
<path d="M5 12h14M12 5l7 7-7 7"/>
|
||||
</svg>
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CareerForm;
|
||||
41
app/components/Career/CareerHero.jsx
Normal file
41
app/components/Career/CareerHero.jsx
Normal file
@ -0,0 +1,41 @@
|
||||
import React from 'react';
|
||||
|
||||
const CareerHero = () => {
|
||||
return (
|
||||
<section
|
||||
className="relative bg-cover bg-center py-6 h-24"
|
||||
style={{ backgroundImage: "url('images/bredcrumb.jpg')" }}
|
||||
>
|
||||
{/* Breadcrumb */}
|
||||
<div className="relative z-10 mb-1 -mt-3">
|
||||
<div className="container mx-auto max-w-none px-4">
|
||||
<nav className="flex items-center space-x-2 text-sm">
|
||||
<a href="/" className="text-white hover:text-yellow-400 underline">Home</a>
|
||||
<span className="text-white">
|
||||
<svg className="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clipRule="evenodd" />
|
||||
</svg>
|
||||
</span>
|
||||
<a href="/about-us" className="text-white hover:text-yellow-400 underline">About Us</a>
|
||||
<span className="text-white">
|
||||
<svg className="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clipRule="evenodd" />
|
||||
</svg>
|
||||
</span>
|
||||
<span className="text-white">Career</span>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Page Title */}
|
||||
<div className="relative z-10 text-center -mt-2">
|
||||
<h1 className="text-4xl md:text-4xl font-bold text-white mb-2">
|
||||
Career
|
||||
</h1>
|
||||
<div className="w-16 h-1 bg-yellow-400 mx-auto"></div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default CareerHero;
|
||||
27
app/components/Career/CareerInfo.jsx
Normal file
27
app/components/Career/CareerInfo.jsx
Normal file
@ -0,0 +1,27 @@
|
||||
import React from 'react';
|
||||
import Image from 'next/image';
|
||||
|
||||
const CareerInfo = () => {
|
||||
return (
|
||||
<div className="lg:w-5/12 relative">
|
||||
<div className="p-6 md:p-8 lg:p-8 text-center lg:text-left">
|
||||
<div className="mb-6">
|
||||
<h2 className="text-gray-700 text-2xl md:text-3xl lg:text-3xl font-semibold leading-tight mb-6">
|
||||
If you are passionate about genomics, we would love to meet you!
|
||||
</h2>
|
||||
<div className="flex justify-center lg:justify-start">
|
||||
<Image
|
||||
src="/images/career.png"
|
||||
alt="Career"
|
||||
width={500}
|
||||
height={400}
|
||||
className="max-w-full h-auto max-h-80 md:max-h-96 lg:max-h-[28rem]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CareerInfo;
|
||||
15
app/components/Career/CareerPage.jsx
Normal file
15
app/components/Career/CareerPage.jsx
Normal file
@ -0,0 +1,15 @@
|
||||
import React from 'react';
|
||||
import CareerHero from './CareerHero'
|
||||
import CareerSection from './CareerSection';
|
||||
|
||||
const CareerPage = () => {
|
||||
return (
|
||||
<div className="page-content contact-us">
|
||||
<CareerHero />
|
||||
<div className="h-6"></div>
|
||||
<CareerSection />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CareerPage;
|
||||
18
app/components/Career/CareerSection.jsx
Normal file
18
app/components/Career/CareerSection.jsx
Normal file
@ -0,0 +1,18 @@
|
||||
import React from 'react';
|
||||
import CareerForm from './CareerForm';
|
||||
import CareerInfo from './CareerInfo';
|
||||
|
||||
const CareerSection = () => {
|
||||
return (
|
||||
<section className="py-10 md:py-16 lg:py-6">
|
||||
<div className="container mx-auto max-w-none px-4">
|
||||
<div className="flex flex-col lg:flex-row gap-6">
|
||||
<CareerInfo />
|
||||
<CareerForm />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default CareerSection;
|
||||
108
app/components/Career/ExperienceSelect.jsx
Normal file
108
app/components/Career/ExperienceSelect.jsx
Normal file
@ -0,0 +1,108 @@
|
||||
'use client';
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
|
||||
const ExperienceSelect = ({ value, onChange, required = false }) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [selectedLabel, setSelectedLabel] = useState('Years of Experience');
|
||||
const dropdownRef = useRef(null);
|
||||
|
||||
const experienceOptions = [
|
||||
{ value: '', label: 'Years of Experience', disabled: true },
|
||||
{ value: '0-1', label: '0-1 years' },
|
||||
{ value: '1-3', label: '1-3 years' },
|
||||
{ value: '3-5', label: '3-5 years' },
|
||||
{ value: '5-8', label: '5-8 years' },
|
||||
{ value: '8-10', label: '8-10 years' },
|
||||
{ value: '10+', label: '10+ years' }
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
const selectedOption = experienceOptions.find(option => option.value === value);
|
||||
if (selectedOption && !selectedOption.disabled) {
|
||||
setSelectedLabel(selectedOption.label);
|
||||
} else {
|
||||
setSelectedLabel('Years of Experience');
|
||||
}
|
||||
}, [value]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event) => {
|
||||
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
|
||||
setIsOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('mousedown', handleClickOutside);
|
||||
return () => {
|
||||
document.removeEventListener('mousedown', handleClickOutside);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleSelect = (option) => {
|
||||
if (!option.disabled) {
|
||||
onChange(option.value);
|
||||
setIsOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="relative w-full" ref={dropdownRef}>
|
||||
<div
|
||||
className={`w-full px-5 py-4 border border-gray-300 rounded-full cursor-pointer flex items-center justify-between bg-white focus-within:ring-2 focus-within:ring-blue-500 focus-within:border-transparent ${
|
||||
isOpen ? 'ring-2 ring-blue-500 border-transparent' : ''
|
||||
}`}
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
>
|
||||
<span className={`${value ? 'text-gray-800' : 'text-gray-500'} pr-4`}>
|
||||
{selectedLabel}
|
||||
</span>
|
||||
<span className={`transform transition-transform duration-200 ${isOpen ? 'rotate-180' : ''}`}>
|
||||
<svg
|
||||
width="16"
|
||||
height="12"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="text-gray-600"
|
||||
>
|
||||
<path
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="1.5"
|
||||
d="M6 8l4 4 4-4"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{isOpen && (
|
||||
<div className="absolute top-full left-0 right-0 mt-1 bg-white border border-gray-200 rounded-lg shadow-lg z-50 max-h-48 overflow-y-auto">
|
||||
{experienceOptions.map((option, index) => {
|
||||
if (option.disabled) return null;
|
||||
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className="px-6 py-3 cursor-pointer hover:bg-gray-50 text-gray-800 transition-colors duration-150"
|
||||
onClick={() => handleSelect(option)}
|
||||
>
|
||||
{option.label}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Hidden input for form validation */}
|
||||
<input
|
||||
type="hidden"
|
||||
name="experience"
|
||||
value={value}
|
||||
required={required}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ExperienceSelect;
|
||||
57
app/components/Career/FileUpload.jsx
Normal file
57
app/components/Career/FileUpload.jsx
Normal file
@ -0,0 +1,57 @@
|
||||
'use client';
|
||||
import React, { useRef } from 'react';
|
||||
|
||||
const FileUpload = ({ onFileChange, currentFile, required = false }) => {
|
||||
const fileInputRef = useRef(null);
|
||||
|
||||
const handleFileChange = (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (file) {
|
||||
// Validate file type
|
||||
const allowedTypes = ['application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'];
|
||||
if (allowedTypes.includes(file.type)) {
|
||||
onFileChange(file);
|
||||
} else {
|
||||
alert('Please upload a PDF, DOC, or DOCX file.');
|
||||
e.target.value = '';
|
||||
}
|
||||
} else {
|
||||
onFileChange(null);
|
||||
}
|
||||
};
|
||||
|
||||
const handleClick = () => {
|
||||
fileInputRef.current?.click();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="relative w-full">
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
type="file"
|
||||
name="resume"
|
||||
accept=".pdf,.doc,.docx"
|
||||
onChange={handleFileChange}
|
||||
required={required}
|
||||
className="hidden"
|
||||
/>
|
||||
|
||||
<div
|
||||
onClick={handleClick}
|
||||
className="w-full px-5 py-4 border border-gray-300 rounded-full cursor-pointer flex items-center justify-between bg-white hover:border-gray-400 focus-within:ring-2 focus-within:ring-blue-500 focus-within:border-transparent transition-colors"
|
||||
>
|
||||
<span className={`${currentFile ? 'text-gray-800' : 'text-gray-500'} pr-4 truncate`}>
|
||||
{currentFile ? currentFile.name : 'Upload Resume'}
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
className="px-3 py-1 text-sm bg-gray-100 border border-gray-300 rounded hover:bg-gray-200 transition-colors whitespace-nowrap"
|
||||
>
|
||||
Choose File
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default FileUpload;
|
||||
125
app/components/Certifications/CertificationsSection.jsx
Normal file
125
app/components/Certifications/CertificationsSection.jsx
Normal file
@ -0,0 +1,125 @@
|
||||
"use client"
|
||||
import React, { useState } from 'react';
|
||||
|
||||
const CertificationModal = ({ isOpen, onClose, certificateImage, title }) => {
|
||||
if (!isOpen) return null;
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4">
|
||||
<div className="w-full max-w-5xl h-[95vh] relative flex flex-col">
|
||||
{/* Close button */}
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="absolute top-4 right-4 w-8 h-8 bg-gray-200 hover:bg-gray-300 rounded-full flex items-center justify-center transition-colors z-10"
|
||||
>
|
||||
<svg className="w-5 h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
{/* Certificate image */}
|
||||
<div className="flex-1 flex items-center justify-center overflow-hidden">
|
||||
<img
|
||||
src={certificateImage}
|
||||
alt={title}
|
||||
className="w-full h-full object-contain"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const CertificationCard = ({ title, description, certificateLink, onOpenModal }) => (
|
||||
<div className="relative group">
|
||||
<div className="p-5 rounded-3xl shadow-sm hover:shadow-lg transition-all duration-300 h-full flex flex-col justify-between" style={{ backgroundColor: '#f2fcfc' }}>
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-teal-700 mb-3 leading-tight text-left">{title}</h3>
|
||||
<p className="text-gray-600 leading-relaxed text-sm mb-5 text-justify">{description}</p>
|
||||
</div>
|
||||
|
||||
{/* Arrow Button */}
|
||||
<div className="flex justify-end">
|
||||
<button
|
||||
onClick={() => onOpenModal(certificateLink, title)}
|
||||
className="w-8 h-8 bg-teal-700 text-white rounded-full flex items-center justify-center hover:bg-teal-800 transition-all duration-300 hover:scale-110 group-hover:shadow-lg"
|
||||
>
|
||||
<svg className="w-3 h-3 transform group-hover:translate-x-1 transition-transform duration-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8l4 4m0 0l-4 4m4-4H3" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const CertificationsSection = () => {
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
const [selectedCertificate, setSelectedCertificate] = useState({ image: '', title: '' });
|
||||
|
||||
const openModal = (certificateImage, title) => {
|
||||
setSelectedCertificate({ image: certificateImage, title });
|
||||
setModalOpen(true);
|
||||
};
|
||||
|
||||
const closeModal = () => {
|
||||
setModalOpen(false);
|
||||
setSelectedCertificate({ image: '', title: '' });
|
||||
};
|
||||
|
||||
const certifications = [
|
||||
{
|
||||
title: "ISO Certificate DNA Sequencing",
|
||||
description: "Certified quality management systems for next-generation sequencing DNA services.",
|
||||
certificateLink: "/images/certificates/iso-dna-sequencing.jpg"
|
||||
},
|
||||
{
|
||||
title: "ISO Certificate RNA Sequencing",
|
||||
description: "Internationally recognized standards for transcriptome analysis and RNA-seq services.",
|
||||
certificateLink: "/images/certificates/iso-rna-sequencing.jpg"
|
||||
},
|
||||
{
|
||||
title: "ISO Certificate Genomics Informatics",
|
||||
description: "Certified bioinformatics processes and data analysis standards for genomic research.",
|
||||
certificateLink: "/images/certificates/iso-genomics-informatics.jpg"
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<section className="py-6 bg-white">
|
||||
<div className="container mx-auto max-w-none px-6">
|
||||
<div className="mb-12">
|
||||
<h2 className="text-4xl font-bold text-teal-700 mb-6">Certifications</h2>
|
||||
<p className="text-gray-600 leading-relaxed text-base text-justify max-w-4xl">
|
||||
Operify Tech is committed to maintaining the highest quality standards with ISO
|
||||
certifications in genomic services.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
{certifications.map((cert, index) => (
|
||||
<CertificationCard
|
||||
key={index}
|
||||
title={cert.title}
|
||||
description={cert.description}
|
||||
certificateLink={cert.certificateLink}
|
||||
onOpenModal={openModal}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Modal */}
|
||||
<CertificationModal
|
||||
isOpen={modalOpen}
|
||||
onClose={closeModal}
|
||||
certificateImage={selectedCertificate.image}
|
||||
title={selectedCertificate.title}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default CertificationsSection;
|
||||
109
app/components/Clients/ClientsSection.jsx
Normal file
109
app/components/Clients/ClientsSection.jsx
Normal file
@ -0,0 +1,109 @@
|
||||
"use client"
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import Image from 'next/image';
|
||||
|
||||
const ClientsSection = () => {
|
||||
const [translateX, setTranslateX] = useState(0);
|
||||
const [isTransitioning, setIsTransitioning] = useState(true);
|
||||
const containerRef = useRef(null);
|
||||
|
||||
const clients = [
|
||||
{ name: "Ashoka University", logo: "/images/client/ashoka.png" },
|
||||
{ name: "BRIC", logo: "/images/client/BRIC.png" },
|
||||
{ name: "CSIR", logo: "/images/client/csir.png" },
|
||||
{ name: "DRDO", logo: "/images/client/drdo.png" },
|
||||
{ name: "DUAC", logo: "/images/client/duac.png" },
|
||||
{ name: "ICMR-NIV", logo: "/images/client/icmr-niv.jpg" },
|
||||
{ name: "IIMR", logo: "/images/client/iimr.png" },
|
||||
{ name: "IISERB", logo: "/images/client/iiserb.png" },
|
||||
{ name: "ILBS", logo: "/images/client/ilbs.jpg" },
|
||||
{ name: "Manipal", logo: "/images/client/manipal.png" },
|
||||
{ name: "Rani", logo: "/images/client/rani.png" },
|
||||
{ name: "OUAT", logo: "/images/client/Ouat.png" },
|
||||
{ name: "AIIMS", logo: "/images/client/aiims.png" },
|
||||
{ name: "IARI", logo: "/images/client/iari.png" },
|
||||
{ name: "KGMU", logo: "/images/client/kgmu.png" },
|
||||
{ name: "BHU", logo: "/images/client/bhu.png" }
|
||||
];
|
||||
|
||||
// Triple the array for seamless infinite loop
|
||||
const infiniteClients = [...clients, ...clients, ...clients];
|
||||
const itemWidth = 100 / 50; // Each item is 1/6 of the container
|
||||
const singleSetWidth = clients.length * itemWidth; // Width of one complete set
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setInterval(() => {
|
||||
setTranslateX(prev => {
|
||||
const newTranslateX = prev + itemWidth;
|
||||
|
||||
// When we reach the end of the second set, reset to the beginning of the second set
|
||||
if (newTranslateX >= singleSetWidth * 1) {
|
||||
// Temporarily disable transition for seamless reset
|
||||
setIsTransitioning(false);
|
||||
setTimeout(() => {
|
||||
setTranslateX(singleSetWidth); // Reset to start of second set
|
||||
setIsTransitioning(true);
|
||||
}, 50);
|
||||
|
||||
return prev;
|
||||
}
|
||||
|
||||
return newTranslateX;
|
||||
});
|
||||
}, 3000);
|
||||
|
||||
return () => clearInterval(timer);
|
||||
}, [itemWidth, singleSetWidth]);
|
||||
|
||||
return (
|
||||
<section className="py-16 bg-gray-100">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="text-center mb-12">
|
||||
<h2 className="text-3xl font-bold text-teal-700 mb-6">
|
||||
Trusted By Leading Institutions
|
||||
</h2>
|
||||
<div className="flex justify-center mb-8">
|
||||
<Image
|
||||
src="/images/homepage-2/Trsuted-Institutions.png"
|
||||
alt="Trusted Institutions"
|
||||
width={250}
|
||||
height={100}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Slider Container */}
|
||||
<div className="overflow-hidden">
|
||||
<div
|
||||
ref={containerRef}
|
||||
className={`flex ${isTransitioning ? 'transition-transform duration-1000 ease-linear' : ''}`}
|
||||
style={{
|
||||
transform: `translateX(-${translateX}%)`,
|
||||
width: `${(infiniteClients.length * 100) / 6}%`
|
||||
}}
|
||||
>
|
||||
{infiniteClients.map((client, index) => (
|
||||
<div
|
||||
key={`${client.name}-${index}`}
|
||||
className="flex-shrink-0 px-4"
|
||||
style={{ width: `${100 / infiniteClients.length}%` }}
|
||||
>
|
||||
<div className="flex justify-center items-center h-24">
|
||||
<Image
|
||||
src={client.logo}
|
||||
alt={client.name}
|
||||
width={300}
|
||||
height={150}
|
||||
className="w-[10rem] h-[6rem] object-contain hover:scale-105 transition-transform duration-300"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default ClientsSection;
|
||||
41
app/components/Company/CompanyHero.jsx
Normal file
41
app/components/Company/CompanyHero.jsx
Normal file
@ -0,0 +1,41 @@
|
||||
import React from 'react';
|
||||
|
||||
const CompanyHero = () => {
|
||||
return (
|
||||
<section
|
||||
className="relative bg-cover bg-center py-6 h-24"
|
||||
style={{ backgroundImage: "url('images/bredcrumb.jpg')" }}
|
||||
>
|
||||
{/* Breadcrumb */}
|
||||
<div className="relative z-10 mb-1 -mt-3">
|
||||
<div className="container mx-auto max-w-none px-4">
|
||||
<nav className="flex items-center space-x-2 text-sm">
|
||||
<a href="/" className="text-white hover:text-yellow-400 underline">Home</a>
|
||||
<span className="text-white">
|
||||
<svg className="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clipRule="evenodd" />
|
||||
</svg>
|
||||
</span>
|
||||
<a href="/about-us" className="text-white hover:text-yellow-400 underline">About Us</a>
|
||||
<span className="text-white">
|
||||
<svg className="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clipRule="evenodd" />
|
||||
</svg>
|
||||
</span>
|
||||
<span className="text-white">Company</span>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Page Title */}
|
||||
<div className="relative z-10 text-center -mt-2">
|
||||
<h1 className="text-4xl md:text-5xl font-bold text-white mb-2">
|
||||
Our Company
|
||||
</h1>
|
||||
<div className="w-16 h-1 bg-yellow-400 mx-auto"></div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default CompanyHero;
|
||||
29
app/components/Company/CompanyIntro.jsx
Normal file
29
app/components/Company/CompanyIntro.jsx
Normal file
@ -0,0 +1,29 @@
|
||||
import React from 'react';
|
||||
|
||||
const CompanyIntro = () => {
|
||||
return (
|
||||
<section className="py-0">
|
||||
<div className="container-fluid">
|
||||
|
||||
{/* Right side content */}
|
||||
<div className="bg-gradient-to-br from-teal-600 to-teal-700 text-white p-8 lg:p-12 flex items-center">
|
||||
<div className="w-full">
|
||||
|
||||
<p className=" leading-relaxed text-justify">
|
||||
Operify Tech is at the forefront of unlocking genomic secrets through advanced
|
||||
next-generation sequencing (NGS) technology that empowers researchers,
|
||||
academicians, and innovators. We are ISO certified for quality management of DNA
|
||||
sequencing, RNA sequencing, and genomics informatics to bring the best to the
|
||||
research and clinical community. Whether you are planning for whole genome sequencing
|
||||
for never-before sequenced genomes or thinking of performing Total RNA sequencing for
|
||||
your experiments, we have got it covered.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default CompanyIntro;
|
||||
74
app/components/Company/OurOfferings.jsx
Normal file
74
app/components/Company/OurOfferings.jsx
Normal file
@ -0,0 +1,74 @@
|
||||
import React from 'react';
|
||||
import Image from 'next/image';
|
||||
|
||||
const OurOfferings = () => {
|
||||
return (
|
||||
<section className="pt-6 bg-white rounded-2xl shadow-lg">
|
||||
<div className="container mx-auto px-4">
|
||||
<h2 className="text-4xl font-bold text-gray-700 text-left pb-2 mb-4">
|
||||
Our Offerings
|
||||
</h2>
|
||||
|
||||
{/* Services paragraph */}
|
||||
<div className="mb-4 text-justify">
|
||||
<p className="text-gray-600 leading-relaxed text-base">
|
||||
Our diverse portfolio covers entire spectrum of services Whole Genome DNA
|
||||
Sequencing, Hi-C Sequencing, Optical mapping, Whole Exome DNA Sequencing,
|
||||
Targeted DNA Sequencing / Gene Panels, Amplicon Sequencing, Transcriptome Sequencing
|
||||
(RNA-seq), Small RNA Sequencing, Single-cell DNA and RNA Sequencing, Microbiome
|
||||
Profiling, Epigenetics Services, Metagenomics Sequencing, SARS CoV-2
|
||||
Sequencing, Bioinformatics Solutions and many more.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Introduction text */}
|
||||
<div className="mb-4 text-justify">
|
||||
<p className="text-gray-600 text-base leading-relaxed">
|
||||
As your trusted ally, we deliver customized solutions for extraction,
|
||||
library preparation, sequencing, and bioinformatics, all from our state-of-the-art New Delhi
|
||||
laboratory. Partner with us for state-of-the-art NGS services on the platform of your choice
|
||||
(Illumina, Oxford Nanopore, PacBio), fast turnaround time and strict QC, advanced or
|
||||
customized Bioinformatics services, and end-to-end support by our experienced scientists.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Team Highlight */}
|
||||
<div className="mb-4 text-justify">
|
||||
<p className="text-gray-600 text-base leading-relaxed">
|
||||
Our team of PhDs, leading each department, ensures unmatched support from
|
||||
initial consultation to post-delivery assistance, providing a seamless experience
|
||||
throughout. Operify Tech is committed to helping you achieve your research goals efficiently
|
||||
and affordably.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Closing Statement */}
|
||||
<div className="text-justify mb-10">
|
||||
<p className="text-gray-600 text-base leading-relaxed">
|
||||
Let's collaborate to create innovative solutions and provide exceptional support on your
|
||||
genomics journey. Explore our services and contact us for your NGS studies.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Centered image with background effect */}
|
||||
<div className="max-w-2xl mx-auto text-center pb-4">
|
||||
<div className="relative">
|
||||
{/* Background gradient circle */}
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
<div className="w-64 h-64 bg-gradient-radial from-teal-50 to-transparent rounded-full opacity-70"></div>
|
||||
</div>
|
||||
<Image
|
||||
src="/images/services1.png"
|
||||
alt="Advanced Laboratory Services"
|
||||
width={600}
|
||||
height={400}
|
||||
className="relative z-10 w-full rounded-lg"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default OurOfferings;
|
||||
51
app/components/Company/VisionMission.jsx
Normal file
51
app/components/Company/VisionMission.jsx
Normal file
@ -0,0 +1,51 @@
|
||||
import React from 'react';
|
||||
import Image from 'next/image';
|
||||
|
||||
const VisionMissionCard = ({ icon, title, description, color, borderColor }) => (
|
||||
<div className="relative z-10 p-4">
|
||||
<div className="flex items-center gap-5 mb-5">
|
||||
<div
|
||||
className={`${color} rounded-xl w-15 h-15 flex items-center justify-center flex-shrink-0`}
|
||||
style={{ width: '60px', height: '60px' }}
|
||||
>
|
||||
<Image src={icon} alt={title} width={30} height={30} className="object-contain" />
|
||||
</div>
|
||||
<h2 className={`text-2xl font-bold ${borderColor === 'border-teal-600' ? 'text-teal-600' : 'text-yellow-500'} tracking-tight`}>
|
||||
{title}
|
||||
</h2>
|
||||
</div>
|
||||
<div className={`text-gray-600 text-base leading-relaxed ml-2 border-l-2 ${borderColor} pl-5`}>
|
||||
{description}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const VisionMission = () => {
|
||||
return (
|
||||
<section className="pt-6 bg-gray-50">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
{/* Vision Section */}
|
||||
<VisionMissionCard
|
||||
icon="/images/service/sight.png"
|
||||
title="Our Vision"
|
||||
description="To decode and discover the intricacies of genomes using the power of Next-Generation Sequencing (NGS) with the highest quality possible. To contribute to taking Indian NGS discoveries to a global scale."
|
||||
color="bg-teal-50"
|
||||
borderColor="border-teal-600"
|
||||
/>
|
||||
|
||||
{/* Mission Section */}
|
||||
<VisionMissionCard
|
||||
icon="/images/service/target.png"
|
||||
title="Our Mission"
|
||||
description="To transform genomics by delivering unparalleled quality and affordability in high-throughput technology services, empowering academia, research, and clinical practices to achieve groundbreaking discoveries and advancements."
|
||||
color="bg-yellow-50"
|
||||
borderColor="border-yellow-500"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default VisionMission;
|
||||
69
app/components/Company/WhyChooseUs.jsx
Normal file
69
app/components/Company/WhyChooseUs.jsx
Normal file
@ -0,0 +1,69 @@
|
||||
import React from 'react';
|
||||
import Image from 'next/image';
|
||||
|
||||
const FeatureCard = ({ icon, title, description, hasBorder = true }) => (
|
||||
<div className={`p-6 h-full ${hasBorder ? 'border-r border-gray-300' : ''}`}>
|
||||
<div className="mb-4">
|
||||
<Image src={icon} alt={title} width={80} height={80} className="object-contain" />
|
||||
</div>
|
||||
<h3 className="text-xl font-semibold text-teal-600 mb-3">
|
||||
{title}
|
||||
</h3>
|
||||
<p className="text-gray-600 text-base leading-relaxed">
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
const WhyChooseUs = () => {
|
||||
const features = [
|
||||
{
|
||||
icon: "/images/service/award.png",
|
||||
title: "Unmatched Quality",
|
||||
description: "Our ISO certification in DNA Sequencing, RNA Sequencing, and Genomics Informatics underscores our dedication to delivering superior technical advice and results that align seamlessly with research objectives and expected outcomes."
|
||||
},
|
||||
{
|
||||
icon: "/images/service/execution.png",
|
||||
title: "Exceptional Value",
|
||||
description: "Our comprehensive NGS and bioinformatics services offer unbeatable value for money. We ensure that our pricing structure never stands in the way of your research goals."
|
||||
},
|
||||
{
|
||||
icon: "/images/service/social-support.png",
|
||||
title: "Dedicated Support",
|
||||
description: "We prioritize your needs, offering prompt technical advice and support. Our fast response times and client-focused approach ensure your research stays on track."
|
||||
},
|
||||
{
|
||||
icon: "/images/service/accurate.png",
|
||||
title: "Proven Reliability",
|
||||
description: "We guarantee timely, accurate results using the latest chemistry, instruments, and software, maintaining the highest standards in our analyses."
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<section>
|
||||
<div className="container mx-auto max-w-none px-4">
|
||||
<div className="rounded-3xl p-8" style={{ backgroundColor: '#f2fcfc' }}>
|
||||
<div className="mb-6">
|
||||
<h2 className="text-4xl font-bold text-gray-600 text-left">
|
||||
Why Choose Operify Tech?
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4">
|
||||
{features.map((feature, index) => (
|
||||
<FeatureCard
|
||||
key={index}
|
||||
icon={feature.icon}
|
||||
title={feature.title}
|
||||
description={feature.description}
|
||||
hasBorder={index < 3} // Remove border from last item
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default WhyChooseUs;
|
||||
246
app/components/ContactPage/ContactForm.js
Normal file
246
app/components/ContactPage/ContactForm.js
Normal file
@ -0,0 +1,246 @@
|
||||
"use client"
|
||||
import React, { useState } from 'react';
|
||||
|
||||
const CustomSelect = ({ options, value, onChange, placeholder, required }) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
className="w-full px-5 py-4 bg-white border border-teal-400 rounded-full text-left focus:outline-none focus:ring-2 focus:ring-teal-500 focus:border-transparent appearance-none text-gray-600"
|
||||
required={required}
|
||||
>
|
||||
<span className={value ? 'text-gray-800' : 'text-gray-500'}>
|
||||
{value || placeholder}
|
||||
</span>
|
||||
<svg
|
||||
className={`absolute right-4 top-1/2 transform -translate-y-1/2 w-4 h-4 transition-transform ${isOpen ? 'rotate-180' : ''}`}
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
{isOpen && (
|
||||
<div className="absolute z-10 w-full mt-1 bg-white border border-gray-300 rounded-lg shadow-lg">
|
||||
{options.map((option, index) => (
|
||||
<button
|
||||
key={index}
|
||||
type="button"
|
||||
disabled={option.disabled}
|
||||
onClick={() => {
|
||||
if (!option.disabled) {
|
||||
onChange(option.value);
|
||||
setIsOpen(false);
|
||||
}
|
||||
}}
|
||||
className={`w-full px-4 py-3 text-left hover:bg-gray-50 first:rounded-t-lg last:rounded-b-lg ${option.disabled ? 'text-gray-400 cursor-not-allowed' : 'text-gray-700'}`}
|
||||
>
|
||||
{option.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const ContactForm = () => {
|
||||
const [formData, setFormData] = useState({
|
||||
name: '',
|
||||
phone: '',
|
||||
email: '',
|
||||
Organisation: '', // Changed to match API field name
|
||||
service_interest: '',
|
||||
message: ''
|
||||
});
|
||||
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const serviceOptions = [
|
||||
{ value: '', label: 'Service and Product of Interest', disabled: true },
|
||||
{ value: 'DNA Sequencing', label: 'DNA Sequencing' },
|
||||
{ value: 'RNA Sequencing', label: 'RNA Sequencing' },
|
||||
{ value: 'Genotyping', label: 'Genotyping' },
|
||||
{ value: 'Bioinformatics', label: 'Bioinformatics' },
|
||||
{ value: 'Other', label: 'Other' }
|
||||
];
|
||||
|
||||
const handleInputChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
[name]: value
|
||||
}));
|
||||
};
|
||||
|
||||
const handleSelectChange = (name, value) => {
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
[name]: value
|
||||
}));
|
||||
};
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
setIsSubmitting(true);
|
||||
|
||||
try {
|
||||
const formDataToSend = new FormData();
|
||||
Object.keys(formData).forEach(key => {
|
||||
if (formData[key]) {
|
||||
formDataToSend.append(key, formData[key]);
|
||||
}
|
||||
});
|
||||
formDataToSend.append('form_type', 'contact');
|
||||
|
||||
const response = await fetch('/api/forms', {
|
||||
method: 'POST',
|
||||
body: formDataToSend,
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
alert(result.message);
|
||||
// Reset form
|
||||
setFormData({
|
||||
name: '',
|
||||
phone: '',
|
||||
email: '',
|
||||
Organisation: '',
|
||||
service_interest: '',
|
||||
message: ''
|
||||
});
|
||||
} else {
|
||||
alert(result.error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error submitting form:', error);
|
||||
alert('Error sending message. Please try again.');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="lg:w-7/12">
|
||||
<div className="p-6 md:p-8 lg:p-10 rounded-3xl" style={{ backgroundColor: '#f2fcfc' }}>
|
||||
<div className="mb-8">
|
||||
<h2 className="text-2xl md:text-3xl font-normal text-teal-700 mb-2">
|
||||
Send a message
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-6">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<input
|
||||
type="text"
|
||||
name="name"
|
||||
value={formData.name}
|
||||
onChange={handleInputChange}
|
||||
placeholder="Your Name"
|
||||
required
|
||||
className="w-full px-5 py-4 bg-white border border-teal-400 rounded-full focus:outline-none focus:ring-2 focus:ring-teal-500 focus:border-transparent placeholder-gray-500"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
type="tel"
|
||||
name="phone"
|
||||
value={formData.phone}
|
||||
onChange={handleInputChange}
|
||||
placeholder="Your Phone"
|
||||
required
|
||||
className="w-full px-5 py-4 bg-white border border-teal-400 rounded-full focus:outline-none focus:ring-2 focus:ring-teal-500 focus:border-transparent placeholder-gray-500"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<CustomSelect
|
||||
options={serviceOptions}
|
||||
value={formData.service_interest}
|
||||
onChange={(value) => handleSelectChange('service_interest', value)}
|
||||
placeholder="Service and Product of Interest"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<input
|
||||
type="email"
|
||||
name="email"
|
||||
value={formData.email}
|
||||
onChange={handleInputChange}
|
||||
placeholder="Your Email"
|
||||
required
|
||||
className="w-full px-5 py-4 bg-white border border-teal-400 rounded-full focus:outline-none focus:ring-2 focus:ring-teal-500 focus:border-transparent placeholder-gray-500"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
type="text"
|
||||
name="Organisation"
|
||||
value={formData.Organisation}
|
||||
onChange={handleInputChange}
|
||||
placeholder="Organisation"
|
||||
required
|
||||
className="w-full px-5 py-4 bg-white border border-teal-400 rounded-full focus:outline-none focus:ring-2 focus:ring-teal-500 focus:border-transparent placeholder-gray-500"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<textarea
|
||||
name="message"
|
||||
value={formData.message}
|
||||
onChange={handleInputChange}
|
||||
rows={3}
|
||||
placeholder="Project description"
|
||||
required
|
||||
className="w-full px-5 py-4 bg-white border border-teal-400 rounded-2xl focus:outline-none focus:ring-2 focus:ring-teal-500 focus:border-transparent resize-vertical placeholder-gray-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="pt-4 flex justify-end">
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isSubmitting}
|
||||
className="inline-flex items-center justify-center px-8 py-3 bg-teal-600 text-white font-medium rounded-full hover:bg-teal-700 transition-all duration-300 group disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
{isSubmitting ? (
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-current"></div>
|
||||
<span>Sending...</span>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<span className="mr-2">Submit</span>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
className="w-4 h-4 group-hover:translate-x-1 transition-transform duration-300"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth={2}
|
||||
>
|
||||
<path d="M5 12h14M12 5l7 7-7 7"/>
|
||||
</svg>
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ContactForm;
|
||||
31
app/components/ContactPage/ContactInfo.js
Normal file
31
app/components/ContactPage/ContactInfo.js
Normal file
@ -0,0 +1,31 @@
|
||||
import React from 'react';
|
||||
import Image from 'next/image';
|
||||
|
||||
const ContactInfo = () => {
|
||||
return (
|
||||
<div className="lg:w-5/12 relative">
|
||||
<div className="p-6 md:p-8 lg:p-8 text-center lg:text-left">
|
||||
<div className="mb-6">
|
||||
<h2 className="text-gray-700 text-2xl md:text-3xl lg:text-4xl font-semibold leading-tight mb-4">
|
||||
Reach out to our team
|
||||
</h2>
|
||||
<p className="text-gray-600 text-sm md:text-base leading-relaxed mb-6">
|
||||
We're here here to help with your research and innovation needs.
|
||||
</p>
|
||||
<div className="flex justify-center lg:justify-start">
|
||||
<div className="relative w-full max-w-lg h-80 md:h-96 lg:h-[28rem]">
|
||||
<Image
|
||||
src="/images/Contact_us.png"
|
||||
alt="Contact Us"
|
||||
fill
|
||||
className="object-contain"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ContactInfo;
|
||||
22
app/components/ContactPage/ContactMap.js
Normal file
22
app/components/ContactPage/ContactMap.js
Normal file
@ -0,0 +1,22 @@
|
||||
import React from 'react';
|
||||
|
||||
const ContactMap = () => {
|
||||
return (
|
||||
<section className="contact-iframe-section mb-8">
|
||||
<div className="w-full p-4">
|
||||
<iframe
|
||||
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d4153.06833268994!2d77.14586737601705!3d28.65493368306228!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x390d03c0d9e73225%3A0xfd48e79d8462e401!2soperifytech!5e1!3m2!1sen!2sin!4v1728660015382!5m2!1sen!2sin"
|
||||
width="100%"
|
||||
height="500"
|
||||
style={{ border: 0 }}
|
||||
allowFullScreen
|
||||
loading="lazy"
|
||||
referrerPolicy="no-referrer-when-downgrade"
|
||||
className="w-full h-64 md:h-80 lg:h-96 rounded-3xl"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default ContactMap;
|
||||
17
app/components/ContactPage/ContactPage.js
Normal file
17
app/components/ContactPage/ContactPage.js
Normal file
@ -0,0 +1,17 @@
|
||||
import React from 'react';
|
||||
import PageTitle from './PageTitle';
|
||||
import ContactSection from './ContactSection';
|
||||
import ContactMap from './ContactMap';
|
||||
|
||||
const ContactPage = () => {
|
||||
return (
|
||||
<div className="page-content contact-us">
|
||||
<PageTitle />
|
||||
<div className="h-6"></div>
|
||||
<ContactSection />
|
||||
<ContactMap />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ContactPage;
|
||||
18
app/components/ContactPage/ContactSection.js
Normal file
18
app/components/ContactPage/ContactSection.js
Normal file
@ -0,0 +1,18 @@
|
||||
import React from 'react';
|
||||
import ContactForm from './ContactForm';
|
||||
import ContactInfo from './ContactInfo';
|
||||
|
||||
const ContactSection = () => {
|
||||
return (
|
||||
<section className="py-10 md:py-16 lg:py-6">
|
||||
<div className="container mx-auto max-w-none px-4">
|
||||
<div className="flex flex-col lg:flex-row gap-6">
|
||||
<ContactInfo />
|
||||
<ContactForm />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default ContactSection;
|
||||
104
app/components/ContactPage/CustomSelect.js
Normal file
104
app/components/ContactPage/CustomSelect.js
Normal file
@ -0,0 +1,104 @@
|
||||
'use client';
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
|
||||
const CustomSelect = ({
|
||||
options,
|
||||
value,
|
||||
onChange,
|
||||
placeholder,
|
||||
required = false
|
||||
}) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [selectedLabel, setSelectedLabel] = useState(placeholder);
|
||||
const dropdownRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
const selectedOption = options.find(option => option.value === value);
|
||||
if (selectedOption && !selectedOption.disabled) {
|
||||
setSelectedLabel(selectedOption.label);
|
||||
} else {
|
||||
setSelectedLabel(placeholder);
|
||||
}
|
||||
}, [value, options, placeholder]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event) => {
|
||||
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
|
||||
setIsOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('mousedown', handleClickOutside);
|
||||
return () => {
|
||||
document.removeEventListener('mousedown', handleClickOutside);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleSelect = (option) => {
|
||||
if (!option.disabled) {
|
||||
onChange(option.value);
|
||||
setIsOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="relative w-full" ref={dropdownRef}>
|
||||
<div
|
||||
className={`w-full px-5 py-4 border border-gray-300 rounded-full cursor-pointer flex items-center justify-between bg-white focus-within:ring-2 focus-within:ring-blue-500 focus-within:border-transparent ${
|
||||
isOpen ? 'ring-2 ring-blue-500 border-transparent' : ''
|
||||
}`}
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
>
|
||||
<span className={`${value ? 'text-gray-800' : 'text-gray-500'} pr-4`}>
|
||||
{selectedLabel}
|
||||
</span>
|
||||
<span className={`transform transition-transform duration-200 ${isOpen ? 'rotate-180' : ''}`}>
|
||||
<svg
|
||||
width="16"
|
||||
height="12"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="text-gray-600"
|
||||
>
|
||||
<path
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="1.5"
|
||||
d="M6 8l4 4 4-4"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{isOpen && (
|
||||
<div className="absolute top-full left-0 right-0 mt-1 bg-white border border-gray-200 rounded-lg shadow-lg z-50 max-h-48 overflow-y-auto">
|
||||
{options.map((option, index) => {
|
||||
if (option.disabled) return null;
|
||||
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className="px-6 py-3 cursor-pointer hover:bg-gray-50 text-gray-800 transition-colors duration-150"
|
||||
onClick={() => handleSelect(option)}
|
||||
>
|
||||
{option.label}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Hidden input for form validation */}
|
||||
<input
|
||||
type="hidden"
|
||||
name="service_interest"
|
||||
value={value}
|
||||
required={required}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CustomSelect;
|
||||
36
app/components/ContactPage/PageTitle.js
Normal file
36
app/components/ContactPage/PageTitle.js
Normal file
@ -0,0 +1,36 @@
|
||||
import Link from 'next/link';
|
||||
import React from 'react';
|
||||
|
||||
const PageTitle = () => {
|
||||
return (
|
||||
<section
|
||||
className="relative bg-cover bg-center py-6 h-24"
|
||||
style={{ backgroundImage: "url('images/bredcrumb.jpg')" }}
|
||||
>
|
||||
{/* Breadcrumb */}
|
||||
<div className="relative z-10 mb-1 -mt-3">
|
||||
<div className="container mx-auto max-w-none px-6">
|
||||
<nav className="flex items-center space-x-2 text-sm">
|
||||
<Link href="/" className="text-white hover:text-yellow-400 underline">Home</Link>
|
||||
<span className="text-white">
|
||||
<svg className="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clipRule="evenodd" />
|
||||
</svg>
|
||||
</span>
|
||||
<span className="text-white">Contact Us</span>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Page Title */}
|
||||
<div className="relative z-10 text-center -mt-2">
|
||||
<h1 className="text-4xl md:text-4xl font-bold text-white mb-2">
|
||||
Contact Us
|
||||
</h1>
|
||||
<div className="w-16 h-1 bg-yellow-400 mx-auto"></div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default PageTitle;
|
||||
155
app/components/Layout/Footer.jsx
Normal file
155
app/components/Layout/Footer.jsx
Normal file
@ -0,0 +1,155 @@
|
||||
// components/Layout/Footer.jsx
|
||||
import React from 'react';
|
||||
import Link from 'next/link';
|
||||
import Image from 'next/image';
|
||||
|
||||
const Footer = () => {
|
||||
return (
|
||||
<footer className="text-white relative overflow-hidden rounded-t-3xl" style={{ backgroundColor: '#2a6564' }}>
|
||||
{/* Background Pattern */}
|
||||
<div className="absolute inset-0 opacity-10">
|
||||
<div className="absolute top-0 right-0 w-96 h-96 transform rotate-45 translate-x-48 -translate-y-48">
|
||||
<div className="w-full h-full border-2 border-white rounded-full"></div>
|
||||
</div>
|
||||
<div className="absolute bottom-0 left-0 w-64 h-64 transform -rotate-12 -translate-x-32 translate-y-32">
|
||||
<div className="w-full h-full border border-white rounded-full"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="container mx-auto px-4 py-16 relative z-10">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8 lg:gap-12">
|
||||
{/* Logo and Address */}
|
||||
<div className="lg:col-span-1">
|
||||
<div className="mb-8">
|
||||
<Image
|
||||
src="/images/Operify-Logo-White.png"
|
||||
alt="Operify Tech"
|
||||
width={180}
|
||||
height={60}
|
||||
style={{ width: 'auto', height: 'auto' }}
|
||||
/>
|
||||
</div>
|
||||
<address className="text-teal-100 not-italic leading-relaxed">
|
||||
<div className="space-y-1">
|
||||
<div className="font-medium text-white">Operify Tech Pvt. Ltd.</div>
|
||||
<div>64-65, Satguru Ram Singh Ji Marg</div>
|
||||
<div>Kirti Nagar Industrial Area</div>
|
||||
<div>New Delhi - 110015</div>
|
||||
</div>
|
||||
</address>
|
||||
|
||||
{/* Social Links */}
|
||||
<div className="flex space-x-4 mt-8">
|
||||
<a
|
||||
href="#"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="w-8 h-8 flex items-center justify-center hover:opacity-80 transition-opacity"
|
||||
>
|
||||
<Image src="/images/homepage-2/Group 11.png" alt="Twitter" width={20} height={20} />
|
||||
</a>
|
||||
<a
|
||||
href="https://www.linkedin.com/company/operify-tech/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="w-8 h-8 flex items-center justify-center hover:opacity-80 transition-opacity"
|
||||
>
|
||||
<Image src="/images/homepage-2/Group 10.png" alt="LinkedIn" width={20} height={20} />
|
||||
</a>
|
||||
<a
|
||||
href="https://www.instagram.com/operifytech_/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="w-8 h-8 flex items-center justify-center hover:opacity-80 transition-opacity"
|
||||
>
|
||||
<Image src="/images/homepage-2/Group 12.png" alt="Instagram" width={20} height={20} />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Services */}
|
||||
<div>
|
||||
<h3 className="text-xl font-semibold mb-6 text-white">Services</h3>
|
||||
<ul className="space-y-3 text-teal-100">
|
||||
<li className="hover:text-white transition-colors cursor-pointer">DNA Sequencing</li>
|
||||
<li className="hover:text-white transition-colors cursor-pointer">RNA Sequencing</li>
|
||||
<li className="hover:text-white transition-colors cursor-pointer">Genotyping</li>
|
||||
<li className="hover:text-white transition-colors cursor-pointer">Bioinformatics Services</li>
|
||||
<li className="hover:text-white transition-colors cursor-pointer">Long Read Sequencing</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Useful Links */}
|
||||
<div>
|
||||
<h3 className="text-xl font-semibold mb-6 text-white">Useful Link</h3>
|
||||
<ul className="space-y-3">
|
||||
<li><Link href="/" className="text-teal-100 hover:text-white transition-colors">Home</Link></li>
|
||||
<li><Link href="#research" className="text-teal-100 hover:text-white transition-colors">Research</Link></li>
|
||||
<li><Link href="#" className="text-teal-100 hover:text-white transition-colors">Health</Link></li>
|
||||
<li><Link href="/sample-submission-guideline" className="text-teal-100 hover:text-white transition-colors">Knowledge Hub</Link></li>
|
||||
<li><Link href="/company" className="text-teal-100 hover:text-white transition-colors">About Us</Link></li>
|
||||
<li><Link href="/contact-us" className="text-teal-100 hover:text-white transition-colors">Contact</Link></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Contact Details */}
|
||||
<div>
|
||||
<h3 className="text-xl font-semibold mb-6 text-white">Contact Details</h3>
|
||||
<div className="space-y-4 text-teal-100">
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="w-5 h-5 flex-shrink-0">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" className="w-5 h-5">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 8l7.89 4.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
||||
</svg>
|
||||
</div>
|
||||
<a href="mailto:info@operifytech.com" className="hover:text-white transition-colors">
|
||||
Info@operifytech.com
|
||||
</a>
|
||||
</div>
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="w-5 h-5 flex-shrink-0">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" className="w-5 h-5">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
|
||||
</svg>
|
||||
</div>
|
||||
<a href="tel:01143046242" className="hover:text-white transition-colors">
|
||||
01143046242
|
||||
</a>
|
||||
</div>
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="w-5 h-5 flex-shrink-0">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" className="w-5 h-5">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
|
||||
</svg>
|
||||
</div>
|
||||
<a href="tel:9319171176" className="hover:text-white transition-colors">
|
||||
9319171176
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bottom Bar */}
|
||||
<div className="bg-gray-100 text-gray-600 relative z-10 rounded-t-2xl">
|
||||
<div className="container mx-auto px-4 py-4">
|
||||
<div className="flex flex-col md:flex-row justify-between items-center text-sm">
|
||||
<p>
|
||||
Copyright © 2024 <span className="text-gray-800 font-medium">Operify</span> All Rights Reserved.
|
||||
</p>
|
||||
<ul className="flex space-x-6 mt-3 md:mt-0">
|
||||
<li><Link href="#" className="hover:text-gray-800 transition-colors">Privacy Policy</Link></li>
|
||||
<li className="text-gray-400">|</li>
|
||||
<li><Link href="#" className="hover:text-gray-800 transition-colors">Term And Condition</Link></li>
|
||||
<li className="text-gray-400">|</li>
|
||||
<li><Link href="#" className="hover:text-gray-800 transition-colors">FAQ</Link></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
};
|
||||
|
||||
export default Footer;
|
||||
914
app/components/Layout/Header.jsx
Normal file
914
app/components/Layout/Header.jsx
Normal file
@ -0,0 +1,914 @@
|
||||
"use client";
|
||||
|
||||
import React, { useState } from "react";
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
|
||||
const Header = () => {
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||
const [openDropdown, setOpenDropdown] = useState([]);
|
||||
|
||||
const toggleDropdown = (dropdownName, event) => {
|
||||
if (event) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
setOpenDropdown(current => {
|
||||
if (current.includes(dropdownName)) {
|
||||
// Remove this dropdown and any child dropdowns
|
||||
return current.filter(name => !name.startsWith(dropdownName));
|
||||
} else {
|
||||
// Add this dropdown
|
||||
return [...current, dropdownName];
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const closeAllMenus = () => {
|
||||
setIsMenuOpen(false);
|
||||
setOpenDropdown([]);
|
||||
};
|
||||
|
||||
return (
|
||||
<header className="bg-white shadow-sm border-b border-gray-100 ">
|
||||
<div className="container max-w-none mx-auto">
|
||||
<div className="flex justify-between items-center px-4 sm:px-6 py-3 sm:py-4">
|
||||
{/* Logo */}
|
||||
<div className="flex-shrink-0">
|
||||
<Link href="/" onClick={closeAllMenus}>
|
||||
<Image
|
||||
src="/images/logo.png"
|
||||
alt="Operify Tech"
|
||||
width={200}
|
||||
height={60}
|
||||
className="h-8 sm:h-10 md:h-12 w-auto"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Desktop Navigation */}
|
||||
<nav className="hidden lg:flex flex-1 justify-start ml-10">
|
||||
<ul className="flex space-x-8 xl:space-x-12">
|
||||
<li className="relative group">
|
||||
<Link
|
||||
href="#"
|
||||
className="font-semibold text-lg py-2 hover:text-teal-700 transition-colors"
|
||||
style={{ color: '#2a6564' }}
|
||||
>
|
||||
Research
|
||||
</Link>
|
||||
{/* <ul className="absolute top-full left-0 bg-white shadow-lg rounded-md py-2 w-48 opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-300 z-50 border border-gray-100">
|
||||
<li className="relative group/dna">
|
||||
<Link
|
||||
href="/dna-sequencing"
|
||||
className="flex items-center justify-between px-4 py-3 text-sm text-gray-700 hover:bg-gray-50 hover:text-teal-600 border-b border-gray-50"
|
||||
>
|
||||
<span className="font-medium">DNA Sequencing</span>
|
||||
<svg className="w-3 h-3 ml-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</Link>
|
||||
<ul className="absolute top-0 left-full bg-white shadow-xl rounded-md py-2 w-64 opacity-0 invisible group-hover/dna:opacity-100 group-hover/dna:visible transition-all duration-300 z-50 border border-gray-100 ml-1">
|
||||
<li className="relative group/wgs">
|
||||
<Link
|
||||
href="/dna-sequencing/whole-genome-sequencing"
|
||||
className="flex items-center justify-between px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-teal-600"
|
||||
>
|
||||
Whole Genome Sequencing
|
||||
<svg className="w-3 h-3 ml-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</Link>
|
||||
<ul className="absolute top-0 left-full bg-white shadow-xl rounded-md py-2 w-72 opacity-0 invisible group-hover/wgs:opacity-100 group-hover/wgs:visible transition-all duration-300 z-50 border border-gray-100 ml-1">
|
||||
<li>
|
||||
<Link
|
||||
href="/dna-sequencing/whole-genome-sequencing"
|
||||
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-teal-600"
|
||||
>
|
||||
Whole Genome Sequencing
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/dna-sequencing/whole-genome-sequencing/denovo"
|
||||
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-teal-600"
|
||||
>
|
||||
Whole Genome Denovo Sequencing
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/dna-sequencing/whole-genome-sequencing/resequencing"
|
||||
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-teal-600"
|
||||
>
|
||||
Whole Genome ReSequencing
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li className="relative group/enrichment">
|
||||
<Link
|
||||
href="/dna-sequencing/enrichment-sequencing"
|
||||
className="flex items-center justify-between px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-teal-600"
|
||||
>
|
||||
Enrichment Sequencing
|
||||
<svg className="w-3 h-3 ml-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</Link>
|
||||
<ul className="absolute top-0 left-full bg-white shadow-xl rounded-md py-2 w-72 opacity-0 invisible group-hover/enrichment:opacity-100 group-hover/enrichment:visible transition-all duration-300 z-50 border border-gray-100 ml-1">
|
||||
<li>
|
||||
<Link
|
||||
href="/dna-sequencing/enrichment-sequencing"
|
||||
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-teal-600"
|
||||
>
|
||||
Enrichment Sequencing
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/dna-sequencing/enrichment-sequencing/whole-exome"
|
||||
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-teal-600"
|
||||
>
|
||||
Whole Exome Sequencing
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/dna-sequencing/enrichment-sequencing/amplicon-sequencing"
|
||||
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-teal-600"
|
||||
>
|
||||
Amplicon Sequencing
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/dna-sequencing/enrichment-sequencing/targeted-sequencing"
|
||||
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-teal-600"
|
||||
>
|
||||
Targeted DNA Sequencing
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/dna-sequencing/single-cell-dna-sequencing"
|
||||
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-teal-600"
|
||||
>
|
||||
Single Cell DNA Sequencing
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/dna-sequencing/metagenomics-sequencing"
|
||||
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-teal-600"
|
||||
>
|
||||
Metagenomics Sequencing
|
||||
</Link>
|
||||
</li>
|
||||
<li className="relative group/epigenomics">
|
||||
<Link
|
||||
href="/dna-sequencing/epigenomics-sequencing"
|
||||
className="flex items-center justify-between px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-teal-600"
|
||||
>
|
||||
Epigenomics Sequencing
|
||||
<svg className="w-3 h-3 ml-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</Link>
|
||||
<ul className="absolute top-0 left-full bg-white shadow-xl rounded-md py-2 w-72 opacity-0 invisible group-hover/epigenomics:opacity-100 group-hover/epigenomics:visible transition-all duration-300 z-50 border border-gray-100 ml-1">
|
||||
<li>
|
||||
<Link
|
||||
href="/dna-sequencing/epigenomics-sequencing"
|
||||
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-teal-600"
|
||||
>
|
||||
Epigenomics Sequencing
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/dna-sequencing/epigenomics-sequencing/wgbs"
|
||||
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-teal-600"
|
||||
>
|
||||
Whole Genome Bisulphite Sequencing
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/dna-sequencing/epigenomics-sequencing/chip-sequencing"
|
||||
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-teal-600"
|
||||
>
|
||||
ChIP (Chromatin immunoprecipitation) Sequencing
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/dna-sequencing/epigenomics-sequencing/atac-sequencing"
|
||||
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-teal-600"
|
||||
>
|
||||
ATAC (Assay for Transposase-Accessible Chromatin) Sequencing
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li className="relative group/genome-mapping">
|
||||
<Link
|
||||
href="/dna-sequencing/genome-mapping"
|
||||
className="flex items-center justify-between px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-teal-600"
|
||||
>
|
||||
Genome Mapping
|
||||
<svg className="w-3 h-3 ml-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</Link>
|
||||
<ul className="absolute top-0 left-full bg-white shadow-xl rounded-md py-2 w-72 opacity-0 invisible group-hover/genome-mapping:opacity-100 group-hover/genome-mapping:visible transition-all duration-300 z-50 border border-gray-100 ml-1">
|
||||
<li>
|
||||
<Link
|
||||
href="/dna-sequencing/genome-mapping"
|
||||
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-teal-600"
|
||||
>
|
||||
Genome Mapping
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/dna-sequencing/genome-mapping/hi-c-mapping"
|
||||
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-teal-600"
|
||||
>
|
||||
High-throughput Chromosome Conformation Capture (Hi-C) Mapping
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/dna-sequencing/genome-mapping/optical-mapping"
|
||||
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-teal-600"
|
||||
>
|
||||
Optical Mapping
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/dna-sequencing/long-read-sequencing"
|
||||
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-teal-600"
|
||||
>
|
||||
Whole Genome Long Read Sequencing
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/dna-sequencing/hybrid-genome-sequencing"
|
||||
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-teal-600"
|
||||
>
|
||||
Hybrid Genome Sequencing
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/dna-sequencing/snp-genotyping"
|
||||
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-teal-600"
|
||||
>
|
||||
SNP-based Genotyping
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/dna-sequencing/microsatellites-ssr-str"
|
||||
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-teal-600"
|
||||
>
|
||||
Microsatellites (SSR/STR) -based genotyping
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li className="relative group/rna">
|
||||
<Link
|
||||
href="/rna-sequencing"
|
||||
className="flex items-center justify-between px-4 py-3 text-sm text-gray-700 hover:bg-gray-50 hover:text-teal-600"
|
||||
>
|
||||
<span className="font-medium">RNA Sequencing</span>
|
||||
<svg className="w-3 h-3 ml-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</Link>
|
||||
<ul className="absolute top-0 left-full bg-white shadow-xl rounded-md py-2 w-80 opacity-0 invisible group-hover/rna:opacity-100 group-hover/rna:visible transition-all duration-300 z-50 border border-gray-100 ml-1">
|
||||
<li>
|
||||
<Link
|
||||
href="/rna-sequencing/whole-transcriptome-sequencing"
|
||||
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-teal-600"
|
||||
>
|
||||
Whole Transcriptome (Total RNA) Sequencing
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/rna-sequencing/mrna-sequencing"
|
||||
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-teal-600"
|
||||
>
|
||||
mRNA Sequencing
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/rna-sequencing/small-rna-sequencing"
|
||||
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-teal-600"
|
||||
>
|
||||
Small RNA (sRNA) sequencing
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/rna-sequencing/lncrna-sequencing"
|
||||
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-teal-600"
|
||||
>
|
||||
lncRNA Sequencing
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/rna-sequencing/metatranscriptomics-sequencing"
|
||||
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-teal-600"
|
||||
>
|
||||
Metatranscriptomics Sequencing
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/rna-sequencing/degradome-sequencing"
|
||||
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-teal-600"
|
||||
>
|
||||
Degradome Sequencing
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/rna-sequencing/iso-sequencing"
|
||||
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-teal-600"
|
||||
>
|
||||
Iso Sequencing using PacBio
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/rna-sequencing/circular-rna-sequencing"
|
||||
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-teal-600"
|
||||
>
|
||||
Circular RNA Sequencing
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/rna-sequencing/single-cell-rna-sequencing"
|
||||
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-teal-600"
|
||||
>
|
||||
Single Cell RNA Sequencing
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul> */}
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/#"
|
||||
className="font-semibold text-lg py-2 hover:text-teal-700 transition-colors"
|
||||
style={{ color: '#2a6564' }}
|
||||
>
|
||||
Health
|
||||
</Link>
|
||||
</li>
|
||||
<li className="relative group">
|
||||
<Link
|
||||
href="/sample-submission-guideline"
|
||||
className="font-semibold text-lg py-2 hover:text-teal-700 transition-colors"
|
||||
style={{ color: '#2a6564' }}
|
||||
>
|
||||
Knowledge Hub
|
||||
</Link>
|
||||
<ul className="absolute top-full left-0 bg-white shadow-lg rounded-md py-2 w-64 opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-300 z-50 border border-gray-100">
|
||||
<li>
|
||||
<Link
|
||||
href="/sample-submission-guideline"
|
||||
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-teal-600"
|
||||
>
|
||||
Sample Submission Guideline
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/sample-initiation-form"
|
||||
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-teal-600"
|
||||
>
|
||||
Sample Initiation Form
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/packaging-shipping-guideline"
|
||||
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-teal-600"
|
||||
>
|
||||
Packaging and Shipping Guideline
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li className="relative group">
|
||||
<Link
|
||||
href="/company"
|
||||
className="font-semibold text-lg py-2 hover:text-teal-700 transition-colors"
|
||||
style={{ color: '#2a6564' }}
|
||||
>
|
||||
About Us
|
||||
</Link>
|
||||
<ul className="absolute top-full left-0 bg-white shadow-lg rounded-md py-2 w-48 opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-300 z-50 border border-gray-100">
|
||||
<li>
|
||||
<Link
|
||||
href="/company"
|
||||
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-teal-600"
|
||||
>
|
||||
Company
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/our-team"
|
||||
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-teal-600"
|
||||
>
|
||||
Our Leadership Team
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/careers"
|
||||
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-teal-600"
|
||||
>
|
||||
Career
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
{/* Right side buttons */}
|
||||
<div className="flex items-center space-x-2 sm:space-x-4">
|
||||
<button className="p-2 text-gray-500 hover:text-teal-600 transition-colors">
|
||||
<svg
|
||||
className="w-4 h-4 sm:w-5 sm:h-5"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<Link
|
||||
href="/contact-us"
|
||||
className="hidden sm:flex text-white px-4 lg:px-6 py-2 sm:py-3 rounded-full hover:opacity-90 transition-all items-center space-x-2 font-medium text-sm lg:text-base"
|
||||
style={{ backgroundColor: '#2a6564' }}
|
||||
onClick={closeAllMenus}
|
||||
>
|
||||
<span className="hidden md:inline">Get In Touch</span>
|
||||
<span className="md:hidden">Contact</span>
|
||||
<svg className="w-3 h-3 sm:w-4 sm:h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<path d="M5 12h14M12 5l7 7-7 7"/>
|
||||
</svg>
|
||||
</Link>
|
||||
{/* Mobile menu button */}
|
||||
<button
|
||||
className="lg:hidden p-2 text-gray-700 hover:text-teal-600 transition-colors"
|
||||
onClick={() => setIsMenuOpen(!isMenuOpen)}
|
||||
>
|
||||
<svg
|
||||
className="w-6 h-6"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
{isMenuOpen ? (
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M6 18L18 6M6 6l12 12"
|
||||
/>
|
||||
) : (
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M4 6h16M4 12h16M4 18h16"
|
||||
/>
|
||||
)}
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Mobile Menu */}
|
||||
{isMenuOpen && (
|
||||
<div className="lg:hidden bg-white border-t border-gray-100">
|
||||
<nav className="px-4 sm:px-6 py-4 max-h-96 overflow-y-auto">
|
||||
<ul className="space-y-2">
|
||||
{/* Research Dropdown */}
|
||||
<li>
|
||||
<button
|
||||
onClick={() => toggleDropdown('research')}
|
||||
className="flex items-center justify-between w-full text-left font-medium py-3 border-b border-gray-100"
|
||||
style={{ color: '#2a6564' }}
|
||||
>
|
||||
<span>Research</span>
|
||||
<svg
|
||||
className={`w-4 h-4 transition-transform duration-200 ${
|
||||
openDropdown.includes('research') ? 'rotate-180' : ''
|
||||
}`}
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M19 9l-7 7-7-7"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
{openDropdown.includes('research') && (
|
||||
<ul className="pl-4 mt-2 space-y-2 pb-3">
|
||||
<li>
|
||||
<button
|
||||
onClick={(e) => toggleDropdown('dna', e)}
|
||||
className="flex items-center justify-between w-full text-left text-sm text-gray-600 hover:text-teal-600 py-2"
|
||||
>
|
||||
<span>DNA Sequencing</span>
|
||||
<svg
|
||||
className={`w-3 h-3 transition-transform duration-200 ${
|
||||
openDropdown.includes('dna') ? 'rotate-180' : ''
|
||||
}`}
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
{openDropdown.includes('dna') && (
|
||||
<ul className="pl-4 mt-2 space-y-1">
|
||||
<li>
|
||||
<Link
|
||||
href="/dna-sequencing/whole-genome-sequencing"
|
||||
className="block text-xs text-gray-500 hover:text-teal-600 py-1"
|
||||
onClick={closeAllMenus}
|
||||
>
|
||||
Whole Genome Sequencing
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/dna-sequencing/enrichment-sequencing"
|
||||
className="block text-xs text-gray-500 hover:text-teal-600 py-1"
|
||||
onClick={closeAllMenus}
|
||||
>
|
||||
Enrichment Sequencing
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/dna-sequencing/single-cell-dna-sequencing"
|
||||
className="block text-xs text-gray-500 hover:text-teal-600 py-1"
|
||||
onClick={closeAllMenus}
|
||||
>
|
||||
Single Cell DNA
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/dna-sequencing/metagenomics-sequencing"
|
||||
className="block text-xs text-gray-500 hover:text-teal-600 py-1"
|
||||
onClick={closeAllMenus}
|
||||
>
|
||||
Metagenomics
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/dna-sequencing/epigenomics-sequencing"
|
||||
className="block text-xs text-gray-500 hover:text-teal-600 py-1"
|
||||
onClick={closeAllMenus}
|
||||
>
|
||||
Epigenomics
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/dna-sequencing/genome-mapping"
|
||||
className="block text-xs text-gray-500 hover:text-teal-600 py-1"
|
||||
onClick={closeAllMenus}
|
||||
>
|
||||
Genome Mapping
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/dna-sequencing/long-read-sequencing"
|
||||
className="block text-xs text-gray-500 hover:text-teal-600 py-1"
|
||||
onClick={closeAllMenus}
|
||||
>
|
||||
Long Read Sequencing
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/dna-sequencing/hybrid-genome-sequencing"
|
||||
className="block text-xs text-gray-500 hover:text-teal-600 py-1"
|
||||
onClick={closeAllMenus}
|
||||
>
|
||||
Hybrid Sequencing
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/dna-sequencing/snp-genotyping"
|
||||
className="block text-xs text-gray-500 hover:text-teal-600 py-1"
|
||||
onClick={closeAllMenus}
|
||||
>
|
||||
SNP Genotyping
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/dna-sequencing/microsatellites-ssr-str"
|
||||
className="block text-xs text-gray-500 hover:text-teal-600 py-1"
|
||||
onClick={closeAllMenus}
|
||||
>
|
||||
Microsatellites
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
)}
|
||||
</li>
|
||||
<li>
|
||||
<button
|
||||
onClick={(e) => toggleDropdown('rna', e)}
|
||||
className="flex items-center justify-between w-full text-left text-sm text-gray-600 hover:text-teal-600 py-2"
|
||||
>
|
||||
<span>RNA Sequencing</span>
|
||||
<svg
|
||||
className={`w-3 h-3 transition-transform duration-200 ${
|
||||
openDropdown.includes('rna') ? 'rotate-180' : ''
|
||||
}`}
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
{openDropdown.includes('rna') && (
|
||||
<ul className="pl-4 mt-2 space-y-1">
|
||||
<li>
|
||||
<Link
|
||||
href="/rna-sequencing/whole-transcriptome-sequencing"
|
||||
className="block text-xs text-gray-500 hover:text-teal-600 py-1"
|
||||
onClick={closeAllMenus}
|
||||
>
|
||||
Whole Transcriptome (Total RNA)
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/rna-sequencing/mrna-sequencing"
|
||||
className="block text-xs text-gray-500 hover:text-teal-600 py-1"
|
||||
onClick={closeAllMenus}
|
||||
>
|
||||
mRNA Sequencing
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/rna-sequencing/small-rna-sequencing"
|
||||
className="block text-xs text-gray-500 hover:text-teal-600 py-1"
|
||||
onClick={closeAllMenus}
|
||||
>
|
||||
Small RNA (sRNA) sequencing
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/rna-sequencing/lncrna-sequencing"
|
||||
className="block text-xs text-gray-500 hover:text-teal-600 py-1"
|
||||
onClick={closeAllMenus}
|
||||
>
|
||||
lncRNA Sequencing
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/rna-sequencing/metatranscriptomics-sequencing"
|
||||
className="block text-xs text-gray-500 hover:text-teal-600 py-1"
|
||||
onClick={closeAllMenus}
|
||||
>
|
||||
Metatranscriptomics
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/rna-sequencing/degradome-sequencing"
|
||||
className="block text-xs text-gray-500 hover:text-teal-600 py-1"
|
||||
onClick={closeAllMenus}
|
||||
>
|
||||
Degradome Sequencing
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/rna-sequencing/iso-sequencing"
|
||||
className="block text-xs text-gray-500 hover:text-teal-600 py-1"
|
||||
onClick={closeAllMenus}
|
||||
>
|
||||
Iso Sequencing (PacBio)
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/rna-sequencing/circular-rna-sequencing"
|
||||
className="block text-xs text-gray-500 hover:text-teal-600 py-1"
|
||||
onClick={closeAllMenus}
|
||||
>
|
||||
Circular RNA Sequencing
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/rna-sequencing/single-cell-rna-sequencing"
|
||||
className="block text-xs text-gray-500 hover:text-teal-600 py-1"
|
||||
onClick={closeAllMenus}
|
||||
>
|
||||
Single Cell RNA
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
)}
|
||||
</li>
|
||||
</ul>
|
||||
)}
|
||||
</li>
|
||||
|
||||
{/* Health */}
|
||||
<li>
|
||||
<Link
|
||||
href="/health"
|
||||
className="block font-medium py-3 border-b border-gray-100"
|
||||
style={{ color: '#2a6564' }}
|
||||
onClick={closeAllMenus}
|
||||
>
|
||||
Health
|
||||
</Link>
|
||||
</li>
|
||||
|
||||
{/* Knowledge Hub Dropdown */}
|
||||
<li>
|
||||
<button
|
||||
onClick={() => toggleDropdown('knowledge')}
|
||||
className="flex items-center justify-between w-full text-left font-medium py-3 border-b border-gray-100"
|
||||
style={{ color: '#2a6564' }}
|
||||
>
|
||||
<span>Knowledge Hub</span>
|
||||
<svg
|
||||
className={`w-4 h-4 transition-transform duration-200 ${
|
||||
openDropdown.includes('knowledge') ? 'rotate-180' : ''
|
||||
}`}
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M19 9l-7 7-7-7"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
{openDropdown.includes('knowledge') && (
|
||||
<ul className="pl-4 mt-2 space-y-2 pb-3">
|
||||
<li>
|
||||
<Link
|
||||
href="/sample-submission-guideline"
|
||||
className="block text-sm text-gray-600 hover:text-teal-600 py-2"
|
||||
onClick={closeAllMenus}
|
||||
>
|
||||
Sample Submission Guideline
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/sample-initiation-form"
|
||||
className="block text-sm text-gray-600 hover:text-teal-600 py-2"
|
||||
onClick={closeAllMenus}
|
||||
>
|
||||
Sample Initiation Form
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/packaging-shipping-guideline"
|
||||
className="block text-sm text-gray-600 hover:text-teal-600 py-2"
|
||||
onClick={closeAllMenus}
|
||||
>
|
||||
Packaging and Shipping Guideline
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
)}
|
||||
</li>
|
||||
|
||||
{/* About Us Dropdown */}
|
||||
<li>
|
||||
<button
|
||||
onClick={() => toggleDropdown('about')}
|
||||
className="flex items-center justify-between w-full text-left font-medium py-3 border-b border-gray-100"
|
||||
style={{ color: '#2a6564' }}
|
||||
>
|
||||
<span>About Us</span>
|
||||
<svg
|
||||
className={`w-4 h-4 transition-transform duration-200 ${
|
||||
openDropdown.includes('about') ? 'rotate-180' : ''
|
||||
}`}
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M19 9l-7 7-7-7"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
{openDropdown.includes('about') && (
|
||||
<ul className="pl-4 mt-2 space-y-2 pb-3">
|
||||
<li>
|
||||
<Link
|
||||
href="/company"
|
||||
className="block text-sm text-gray-600 hover:text-teal-600 py-2"
|
||||
onClick={closeAllMenus}
|
||||
>
|
||||
Company
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/our-team"
|
||||
className="block text-sm text-gray-600 hover:text-teal-600 py-2"
|
||||
onClick={closeAllMenus}
|
||||
>
|
||||
Our Leadership Team
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/careers"
|
||||
className="block text-sm text-gray-600 hover:text-teal-600 py-2"
|
||||
onClick={closeAllMenus}
|
||||
>
|
||||
Career
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
)}
|
||||
</li>
|
||||
|
||||
{/* Mobile Contact Button */}
|
||||
<li className="pt-4">
|
||||
<Link
|
||||
href="/contact-us"
|
||||
className="flex w-full text-white px-4 py-3 rounded-md hover:opacity-90 transition-all items-center justify-center space-x-2 font-medium"
|
||||
style={{ backgroundColor: '#2a6564' }}
|
||||
onClick={closeAllMenus}
|
||||
>
|
||||
<span>Get In Touch</span>
|
||||
<svg className="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<path d="M5 12h14M12 5l7 7-7 7"/>
|
||||
</svg>
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
};
|
||||
|
||||
export default Header;
|
||||
30
app/components/Layout/PageLayout.jsx
Normal file
30
app/components/Layout/PageLayout.jsx
Normal file
@ -0,0 +1,30 @@
|
||||
// components/Layout/PageLayout.jsx
|
||||
import React from 'react';
|
||||
import Header from './Header';
|
||||
import Footer from './Footer';
|
||||
|
||||
export default function PageLayout({ children, fixedHeader = false }) {
|
||||
if (fixedHeader) {
|
||||
return (
|
||||
<div className="min-h-screen bg-white">
|
||||
<div className="fixed top-0 left-0 right-0 z-50">
|
||||
<Header />
|
||||
</div>
|
||||
<main className="pt-[60px] sm:pt-[65px] md:pt-[80px]">
|
||||
{children}
|
||||
</main>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-white">
|
||||
<Header />
|
||||
<main>
|
||||
{children}
|
||||
</main>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
51
app/components/PackagingShipping/ContentSection.jsx
Normal file
51
app/components/PackagingShipping/ContentSection.jsx
Normal file
@ -0,0 +1,51 @@
|
||||
// components/PackagingShipping/ContentSection.jsx
|
||||
'use client';
|
||||
import React, { useState } from 'react';
|
||||
import SidebarNavigation from './SidebarNavigation';
|
||||
import GeneralGuidelines from './GeneralGuidelines';
|
||||
import PackagingGuideline from './PackagingGuideline';
|
||||
import DNASamples from './DNASamples';
|
||||
import RNASamples from './RNASamples';
|
||||
import ShippingTemperatureTable from './ShippingTemperatureTable';
|
||||
import ShippingSchedule from './ShippingSchedule';
|
||||
|
||||
const ContentSection = () => {
|
||||
const [activeSection, setActiveSection] = useState('general-content');
|
||||
|
||||
const renderContent = () => {
|
||||
const sections = {
|
||||
'general-content': <GeneralGuidelines />,
|
||||
'packaging-content': <PackagingGuideline />,
|
||||
'dna-content': <DNASamples />,
|
||||
'rna-content': <RNASamples />,
|
||||
'temperature-content': <ShippingTemperatureTable />,
|
||||
'schedule-content': <ShippingSchedule />
|
||||
};
|
||||
return sections[activeSection] || <GeneralGuidelines />;
|
||||
};
|
||||
|
||||
return (
|
||||
<section className="bg-white py-5">
|
||||
<div className="container mx-auto max-w-none px-4 xl:px-8">
|
||||
<div className="bg-white p-4 md:p-6">
|
||||
<div className="grid xl:grid-cols-[280px_1fr] gap-8">
|
||||
{/* LEFT SIDEBAR */}
|
||||
<div className="xl:sticky xl:top-8">
|
||||
<SidebarNavigation
|
||||
activeSection={activeSection}
|
||||
onSectionChange={setActiveSection}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* RIGHT CONTENT */}
|
||||
<div>
|
||||
{renderContent()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default ContentSection;
|
||||
49
app/components/PackagingShipping/DNASamples.jsx
Normal file
49
app/components/PackagingShipping/DNASamples.jsx
Normal file
@ -0,0 +1,49 @@
|
||||
// components/PackagingShipping/DNASamples.jsx
|
||||
import React from 'react';
|
||||
|
||||
const DNASamples = () => {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h3 className="text-2xl font-semibold text-gray-700 mb-6">Shipping of DNA Samples</h3>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col lg:flex-row gap-6">
|
||||
<div className="flex-1 space-y-4">
|
||||
<p className="text-gray-600 leading-relaxed">
|
||||
Picogreen quantification is advised from the client, in absence of which
|
||||
an agarose Gel Electrophoresis and Nanodrop quantification must be
|
||||
shared. Samples with A260/280 ratio values of ~1.8 are considered "pure"
|
||||
for DNA and will be accepted for processing further.
|
||||
</p>
|
||||
|
||||
<p className="text-gray-600 leading-relaxed">
|
||||
For large-scale projects, please submit DNA samples in strip tubes or in
|
||||
well-sealed 96-well PCR plates with semi- or fully-skirted edges (we
|
||||
recommend Eppendorf twin.tec PCR plate 96 LoBind). Arrange samples in a
|
||||
column format (e.g., A1, B1, C1, D1, ..., A2, B2, C2, D2, ...) in
|
||||
contiguous wells. Avoid skipping wells, rows, or columns.
|
||||
</p>
|
||||
|
||||
<p className="text-gray-600 leading-relaxed">
|
||||
DNA samples in 70% ethanol can be transported at room temperature, while
|
||||
samples in H2O or TE buffer should be transported with ice packs (e.g.,
|
||||
"blue ice").
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="lg:w-96 flex justify-center">
|
||||
<div className="text-center">
|
||||
<img
|
||||
src="/images/dna.jpg"
|
||||
alt="DNA Sample Order in 96-Well Plates"
|
||||
className="w-full max-w-96 h-48 object-cover rounded-lg shadow-md"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DNASamples;
|
||||
38
app/components/PackagingShipping/GeneralGuidelines.jsx
Normal file
38
app/components/PackagingShipping/GeneralGuidelines.jsx
Normal file
@ -0,0 +1,38 @@
|
||||
import React from 'react';
|
||||
|
||||
const GeneralGuidelines = () => {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h3 className="text-2xl font-semibold mb-6" style={{ color: '#555555' }}>General Guidelines</h3>
|
||||
</div>
|
||||
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h6 className="text-lg font-medium text-teal-600 mb-3">Sample Submission Form</h6>
|
||||
<p className="leading-relaxed" style={{ color: '#555555' }}>
|
||||
When sending samples, please include the standard sample submission form
|
||||
filled out through our company website or sent through mail (landscape
|
||||
orientation, all on one page). We can only process samples if we receive a
|
||||
hard copy of your submission form along with them. Ensure that the sample
|
||||
names and quantities on the information sheet exactly match the sample names
|
||||
and quantities actually sent.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h6 className="text-lg font-medium text-teal-600 mb-3">Sample Naming</h6>
|
||||
<p className="leading-relaxed" style={{ color: '#555555' }}>
|
||||
Please use unique sample names consisting of 4-6 characters, using uppercase
|
||||
letters, numbers, and underscores only. Do not write the sample name and
|
||||
other information directly on the tube wall or tube cover with an oil pen.
|
||||
Instead, use a black permanent marker to write sample names on the top and
|
||||
side of each tube.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default GeneralGuidelines;
|
||||
49
app/components/PackagingShipping/PackagingGuideline.jsx
Normal file
49
app/components/PackagingShipping/PackagingGuideline.jsx
Normal file
@ -0,0 +1,49 @@
|
||||
import React from 'react';
|
||||
|
||||
const PackagingGuideline = () => {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h3 className="text-2xl font-semibold mb-6" style={{ color: '#555555' }}>Packaging Guideline</h3>
|
||||
</div>
|
||||
|
||||
<div className="space-y-6">
|
||||
<p className="leading-relaxed" style={{ color: '#555555' }}>
|
||||
Seal the tubes with parafilm for transportation. To prevent the tubes from
|
||||
being crushed and broken during transit (leading to sample loss), insert
|
||||
sample tubes into 50 ml centrifuge tubes (or other rigid supports), which
|
||||
can also be packed with cotton, foam, etc.
|
||||
</p>
|
||||
|
||||
<p className="leading-relaxed" style={{ color: '#555555' }}>
|
||||
To prevent sample loss and/or cross-contamination, tightly seal all wells of
|
||||
the plate with an adhesive sheet or foil. Protect the plates or strip tubes
|
||||
in a sturdy box with plenty of cushioning. Sample shipments of plates should
|
||||
be carried out on frozen blue ice or dry ice to ensure that the samples
|
||||
remain frozen during shipment.
|
||||
</p>
|
||||
|
||||
<p className="leading-relaxed" style={{ color: '#555555' }}>
|
||||
To prevent sample loss and cross-contamination, we recommend securely
|
||||
sealing all wells of the plate with an adhesive sheet or foil. Place the
|
||||
plates or strip tubes in a sturdy box with ample cushioning. Ship the
|
||||
samples with a surplus of frozen blue ice blocks or dry ice to ensure they
|
||||
remain frozen throughout shipment.
|
||||
</p>
|
||||
|
||||
<p className="leading-relaxed" style={{ color: '#555555' }}>
|
||||
For leak prevention and to avoid cross-contamination, use one of the
|
||||
following sealing methods:<br />
|
||||
<span className="font-medium">(a)</span> Cap the wells with matching 8-strip
|
||||
caps, ensuring a tight seal. These caps are typically ordered separately
|
||||
from the plates.<br />
|
||||
<span className="font-medium">(b)</span> For foil seals, use a heat seal (preferred method)
|
||||
like "Thermo Scientific Easy Peel Heat Sealing Foil" that allows for
|
||||
resealing, or adhesive aluminum foil seals such as "AlumaSeal CS Film."
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PackagingGuideline;
|
||||
15
app/components/PackagingShipping/PackagingShippingPage.jsx
Normal file
15
app/components/PackagingShipping/PackagingShippingPage.jsx
Normal file
@ -0,0 +1,15 @@
|
||||
// components/PackagingShipping/PackagingShippingPage.jsx
|
||||
import React from 'react';
|
||||
import PageTitle from './PageTitle';
|
||||
import ContentSection from './ContentSection';
|
||||
|
||||
const PackagingShippingPage = () => {
|
||||
return (
|
||||
<div className="page-content">
|
||||
<PageTitle />
|
||||
<ContentSection />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PackagingShippingPage;
|
||||
41
app/components/PackagingShipping/PageTitle.jsx
Normal file
41
app/components/PackagingShipping/PageTitle.jsx
Normal file
@ -0,0 +1,41 @@
|
||||
import React from 'react';
|
||||
|
||||
const PageTitle = () => {
|
||||
return (
|
||||
<section
|
||||
className="relative bg-cover bg-center py-4 sm:py-6 h-auto sm:h-32 md:h-40 lg:h-24 min-h-[120px] sm:min-h-[140px]"
|
||||
style={{ backgroundImage: "url('images/bredcrumb.jpg')" }}
|
||||
>
|
||||
{/* Breadcrumb */}
|
||||
<div className="relative z-10 mb-2 sm:mb-1 pt-2 sm:pt-0 sm:-mt-3 lg:-mt-3">
|
||||
<div className="container mx-auto max-w-none px-4">
|
||||
<nav className="flex flex-wrap items-center gap-1 sm:gap-2 text-xs sm:text-sm lg:text-sm">
|
||||
<a href="/" className="text-white hover:text-yellow-400 underline whitespace-nowrap">Home</a>
|
||||
<span className="text-white flex-shrink-0">
|
||||
<svg className="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clipRule="evenodd" />
|
||||
</svg>
|
||||
</span>
|
||||
<a href="/sample-submission-guideline" className="text-white hover:text-yellow-400 underline whitespace-nowrap">Knowledge Hub</a>
|
||||
<span className="text-white flex-shrink-0">
|
||||
<svg className="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clipRule="evenodd" />
|
||||
</svg>
|
||||
</span>
|
||||
<span className="text-white">Packaging and Shipping Guideline</span>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Page Title */}
|
||||
<div className="relative z-10 text-center pb-2 sm:pb-0 sm:-mt-2 lg:mt-4">
|
||||
<h1 className="text-lg sm:text-2xl md:text-3xl lg:text-4xl xl:text-4xl font-bold text-white mb-2 px-4 leading-tight">
|
||||
Packaging and Shipping Guideline
|
||||
</h1>
|
||||
<div className="w-12 sm:w-14 md:w-16 lg:w-16 h-1 bg-yellow-400 mx-auto"></div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default PageTitle;
|
||||
46
app/components/PackagingShipping/RNASamples.jsx
Normal file
46
app/components/PackagingShipping/RNASamples.jsx
Normal file
@ -0,0 +1,46 @@
|
||||
import React from 'react';
|
||||
|
||||
const RNASamples = () => {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h3 className="text-2xl font-semibold mb-6" style={{ color: '#555555' }}>Shipping of RNA Samples</h3>
|
||||
</div>
|
||||
|
||||
<div className="space-y-6">
|
||||
<p className="leading-relaxed" style={{ color: '#555555' }}>
|
||||
Bioanalyzer QC report is advised to be shared from the client's end, in the
|
||||
absence of which an agarose Gel Electrophoresis and Nanodrop quantification
|
||||
to confirm the integrity of RNA must be shared. Samples with A260/280 ratio
|
||||
values of ~1.8 are considered "pure" for DNA and will be accepted for
|
||||
processing further.
|
||||
</p>
|
||||
|
||||
<p className="leading-relaxed" style={{ color: '#555555' }}>
|
||||
We require Bioanalyzer traces (or similar) for all customer-submitted
|
||||
sequencing libraries and total RNA samples. If traces are not provided, we
|
||||
will perform Bioanalyzer QC for an additional fee. If you can supply traces,
|
||||
please include them into the shipment in hard copy. Also, ensure that your
|
||||
samples meet the specified sample or library requirements [LINK].
|
||||
</p>
|
||||
|
||||
<p className="leading-relaxed" style={{ color: '#555555' }}>
|
||||
For large-scale projects, RNA samples can be submitted in strip tubes with
|
||||
individually attached RNase-free caps. Pack the strips into racks (e.g.,
|
||||
empty pipet tip boxes) and ensure they are secured to prevent movement
|
||||
during transport.
|
||||
</p>
|
||||
|
||||
<p className="leading-relaxed" style={{ color: '#555555' }}>
|
||||
RNA, cells, bacteria, and frozen tissue samples should be stored in liquid
|
||||
nitrogen for rapid freezing and then transported with dry ice. For longer
|
||||
shipments, RNA samples can also be successfully shipped dry at room
|
||||
temperature after LiCl/ethanol precipitation and ethanol washes; make sure
|
||||
to mark the pellet's position on the tubes.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default RNASamples;
|
||||
61
app/components/PackagingShipping/ShippingSchedule.jsx
Normal file
61
app/components/PackagingShipping/ShippingSchedule.jsx
Normal file
@ -0,0 +1,61 @@
|
||||
import React from 'react';
|
||||
|
||||
const ShippingSchedule = () => {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h3 className="text-2xl font-semibold mb-6" style={{ color: '#555555' }}>Shipping Schedule and Address</h3>
|
||||
</div>
|
||||
|
||||
<div className="space-y-6">
|
||||
<p className="leading-relaxed" style={{ color: '#555555' }}>
|
||||
Before sending your samples, please notify us promptly by mail or by
|
||||
completing the form online, including the Sample Initiation Form. This helps
|
||||
us register and process your samples efficiently upon arrival. As we do not
|
||||
receive packages on weekends, ensure your samples arrive on a weekday. Avoid
|
||||
shipping samples just before weekends (e.g., on a Thursday for Friday
|
||||
arrival) or the day before a holiday.
|
||||
</p>
|
||||
|
||||
<p className="leading-relaxed font-medium" style={{ color: '#555555' }}>
|
||||
We highly recommend using "Priority Overnight Shipping" for morning
|
||||
deliveries, as it is generally more reliable.
|
||||
</p>
|
||||
|
||||
<p className="leading-relaxed" style={{ color: '#555555' }}>
|
||||
We can pick up the sample from your institution (additional logistic charges
|
||||
will be applicable) or you can ship/drop samples at the mentioned address:
|
||||
</p>
|
||||
|
||||
<div >
|
||||
<div className="leading-relaxed" style={{ color: '#555555' }}>
|
||||
<div className="font-semibold text-lg text-teal-700 mb-3">
|
||||
Operify Tech Pvt.Ltd.
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<div>64-65, Satguru Ram Singh Ji Marg,</div>
|
||||
<div>Kirti Nagar Industrial Area,</div>
|
||||
<div>New Delhi- 110015</div>
|
||||
</div>
|
||||
<div className="mt-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-medium">Phone:</span>
|
||||
<a href="tel:9319171176" className="text-teal-600 hover:text-teal-800">
|
||||
9319171176
|
||||
</a>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-medium">Email:</span>
|
||||
<a href="mailto:Info@operifytech.com" className="text-teal-600 hover:text-teal-800">
|
||||
Info@operifytech.com
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ShippingSchedule;
|
||||
186
app/components/PackagingShipping/ShippingTemperatureTable.jsx
Normal file
186
app/components/PackagingShipping/ShippingTemperatureTable.jsx
Normal file
@ -0,0 +1,186 @@
|
||||
'use client';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
const TooltipIcon = ({ text, rowIndex, totalRows }) => {
|
||||
const [showTooltip, setShowTooltip] = useState(false);
|
||||
|
||||
if (!text) return null;
|
||||
|
||||
return (
|
||||
<div className="relative inline-block ml-1">
|
||||
<span
|
||||
className="inline-flex items-center justify-center w-3 h-3 text-xs font-bold text-teal-600 border border-teal-600 rounded-full cursor-pointer hover:bg-teal-600 hover:text-white transition-all duration-200"
|
||||
onMouseEnter={() => setShowTooltip(true)}
|
||||
onMouseLeave={() => setShowTooltip(false)}
|
||||
>
|
||||
i
|
||||
</span>
|
||||
|
||||
{showTooltip && (
|
||||
<div className="absolute left-full top-1/2 transform -translate-y-1/2 ml-2 px-3 py-2 text-xs text-white bg-gray-800 rounded-md shadow-lg z-50 w-64 text-left">
|
||||
{text}
|
||||
<div className="absolute right-full top-1/2 transform -translate-y-1/2 w-0 h-0 border-t-4 border-b-4 border-r-4 border-transparent border-r-gray-800"></div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const ShippingTemperatureTable = () => {
|
||||
const shippingData = [
|
||||
{
|
||||
sampleType: 'Blood',
|
||||
tooltip: 'Collect sample in 2mL EDTA tube for DNA, Collect sample in 2.5mL paxgen/tempus tube for RNA',
|
||||
volume: '2/2.5ml',
|
||||
conditions: 'DNA- 4 Degree (Cool Packs)/ RNA-Dry ice, overnight'
|
||||
},
|
||||
{
|
||||
sampleType: 'Plant Tissue',
|
||||
tooltip: 'Slice tissue into small pieces (<0.5cm) using a sterile scalpel and submerged in 100% Ethanol.',
|
||||
volume: '1gm',
|
||||
conditions: '4 Degree (Cool Packs)'
|
||||
},
|
||||
{
|
||||
sampleType: 'Soil Samples',
|
||||
tooltip: 'Soil samples are collected and then immediately transferred to 4 degree',
|
||||
volume: '100 mg',
|
||||
conditions: '4 Degree (Cool Packs)'
|
||||
},
|
||||
{
|
||||
sampleType: 'Fecal Samples',
|
||||
tooltip: 'Fecal samples are collected then immediately transferred to 4 degree',
|
||||
volume: '100 mg',
|
||||
conditions: '4 Degree (Cool Packs)'
|
||||
},
|
||||
{
|
||||
sampleType: 'Bacterial Culture',
|
||||
tooltip: 'Collect an appropriate amount of bacterial liquid into a 50 mL centrifuge tube, centrifugate at a low speed (3000-5000g/10min) with a 4 °C horizontal centrifuge to collect bacteria, and remove the culture medium as clean as possible. Add 5-10 mL sterile water or PBS solution to wash twice, then transfer it to a 1.5 mL or 2.0 mL centrifuge tube, centrifuge at 1500 rpm at 4 °C for 10min, remove the supernatant, and retain the precipitated bacteria.',
|
||||
volume: '',
|
||||
conditions: '4 Degree (Cool Packs)'
|
||||
},
|
||||
{
|
||||
sampleType: 'Water',
|
||||
tooltip: 'Samples should be taken from at least 10-20 mL of water or slightly turbid water in falcon tube',
|
||||
volume: '50 mL',
|
||||
conditions: '4 Degree (Cool Packs)'
|
||||
},
|
||||
{
|
||||
sampleType: 'Plasma/ Serum',
|
||||
tooltip: 'Plasma is separated from blood using 4 degree centrifugation, aliquoted in 2ml cryovials and Stored at -80 degrees',
|
||||
volume: '10 mL',
|
||||
conditions: 'Dry Ice'
|
||||
},
|
||||
{
|
||||
sampleType: 'Animal Tissue',
|
||||
tooltip: 'Slice tissue into small pieces (<0.5cm) using a sterile scalpel and submerged in 100% Ethanol.',
|
||||
volume: '1gm',
|
||||
conditions: '4 Degree (Cool Packs)'
|
||||
},
|
||||
{
|
||||
sampleType: 'Cell',
|
||||
tooltip: 'The cells in the culture bottle/dish were gently blown and mixed with a pipette gun, and transferred to a 15 mL centrifuge tube, horizontal centrifuge, centrifuge 400g~1000g at 4 °C for 5-10 minutes to collect cells and discard the supernatant, Carefully wash the flake sediment twice with precooled PBS, place it on ice, and discard the supernatant.',
|
||||
volume: '1x10⁶ cells',
|
||||
conditions: 'Dry ice, overnight'
|
||||
},
|
||||
{
|
||||
sampleType: 'Fresh Tissue',
|
||||
tooltip: 'The fresh tissue should be fixed with formaldehyde for no more than 24h. If a pretreatment kit is used, the fixing time shall be in accordance with the requirements of the kit. FFPE chips can be transported at room temperature.',
|
||||
volume: '10 mg',
|
||||
conditions: 'Room temperature'
|
||||
},
|
||||
{
|
||||
sampleType: 'FFPE block/ slides',
|
||||
tooltip: '',
|
||||
volume: '>20% tumor content',
|
||||
conditions: 'Room temperature'
|
||||
},
|
||||
{
|
||||
sampleType: 'FFPE block/ slides',
|
||||
tooltip: '',
|
||||
volume: '≥ 4 FFPE slides, thickness 5 to 20 µm, area >150 mm²',
|
||||
conditions: 'Room temperature'
|
||||
},
|
||||
{
|
||||
sampleType: 'Swabs',
|
||||
tooltip: 'Collect the specimen with the swab. Aseptically unscrew and remove the cap from the transport tube. Insert the swab into the transport tube and break the swab shaft at the indicated breakpoint and screw the cap on tightly.',
|
||||
volume: '2 tubes/sample, 1 swap/tube',
|
||||
conditions: '4 Degree (Cool Packs)'
|
||||
},
|
||||
{
|
||||
sampleType: 'Saliva',
|
||||
tooltip: 'As per the kit manufacture Guideline',
|
||||
volume: '1 mL',
|
||||
conditions: 'Dry ice'
|
||||
},
|
||||
{
|
||||
sampleType: 'Bodily Fluids (gDNA)',
|
||||
tooltip: 'As per the kit manufacture Guideline',
|
||||
volume: '500 µL',
|
||||
conditions: '4 Degree (Cool Packs)'
|
||||
},
|
||||
{
|
||||
sampleType: 'Bodily Fluids (cell free DNA)',
|
||||
tooltip: 'As per the kit manufacture Guideline',
|
||||
volume: '500-10,000 µL',
|
||||
conditions: 'Dry ice'
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h3 className="text-2xl font-semibold text-gray-700 mb-6">Shipping Temperature and Condition</h3>
|
||||
</div>
|
||||
|
||||
<div className="space-y-6">
|
||||
<p className="text-gray-600 leading-relaxed">
|
||||
Before sending your samples, please ensure the shipping conditions, storage
|
||||
temperature and requirements based on sample type:
|
||||
</p>
|
||||
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full border-collapse border border-gray-300 text-sm">
|
||||
<colgroup>
|
||||
<col style={{width: '25%'}} />
|
||||
<col style={{width: '25%'}} />
|
||||
<col style={{width: '50%'}} />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr className="bg-teal-50">
|
||||
<th className="border border-gray-300 px-3 py-2 text-left font-semibold text-teal-700">
|
||||
Sample Type
|
||||
</th>
|
||||
<th className="border border-gray-300 px-3 py-2 text-left font-semibold text-teal-700">
|
||||
Volume/Conc. Required
|
||||
</th>
|
||||
<th className="border border-gray-300 px-3 py-2 text-left font-semibold text-teal-700">
|
||||
Shipping Conditions
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{shippingData.map((row, index) => (
|
||||
<tr key={index} className={index % 2 === 1 ? 'bg-gray-50' : 'bg-white'}>
|
||||
<td className="border border-gray-300 px-3 py-2 align-top" style={{ color: '#555555' }}>
|
||||
<div className="flex items-start">
|
||||
<span>{row.sampleType}</span>
|
||||
<TooltipIcon text={row.tooltip} rowIndex={index} totalRows={shippingData.length} />
|
||||
</div>
|
||||
</td>
|
||||
<td className="border border-gray-300 px-3 py-2 align-top" style={{ color: '#555555' }}>
|
||||
{row.volume}
|
||||
</td>
|
||||
<td className="border border-gray-300 px-3 py-2 align-top" style={{ color: '#555555' }}>
|
||||
{row.conditions}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ShippingTemperatureTable;
|
||||
39
app/components/PackagingShipping/SidebarNavigation.jsx
Normal file
39
app/components/PackagingShipping/SidebarNavigation.jsx
Normal file
@ -0,0 +1,39 @@
|
||||
import React from 'react';
|
||||
|
||||
const SidebarNavigation = ({ activeSection, onSectionChange }) => {
|
||||
const items = [
|
||||
{ id: 'general-content', label: '1. General Guidelines' },
|
||||
{ id: 'packaging-content', label: '2. Packaging Guideline' },
|
||||
{ id: 'dna-content', label: '3. Shipping of DNA Samples' },
|
||||
{ id: 'rna-content', label: '4. Shipping of RNA Samples' },
|
||||
{ id: 'temperature-content', label: '5. Shipping Condition' },
|
||||
{ id: 'schedule-content', label: '6. Shipping Schedule and Address' }
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
{items.map((item) => (
|
||||
<button
|
||||
key={item.id}
|
||||
onClick={() => onSectionChange(item.id)}
|
||||
className={`w-full text-left px-6 py-4 text-base font-medium rounded-full transition-all duration-300 border-2 ${
|
||||
item.id === 'temperature-content' ? '' : 'whitespace-nowrap'
|
||||
} ${
|
||||
activeSection === item.id
|
||||
? 'text-white shadow-lg border-transparent'
|
||||
: 'bg-white text-gray-600 border-transparent hover:border-orange-400'
|
||||
}`}
|
||||
style={
|
||||
activeSection === item.id
|
||||
? { backgroundColor: '#ffa72a' }
|
||||
: {}
|
||||
}
|
||||
>
|
||||
{item.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SidebarNavigation;
|
||||
46
app/components/Research/AnimalResearch.jsx
Normal file
46
app/components/Research/AnimalResearch.jsx
Normal file
@ -0,0 +1,46 @@
|
||||
import Image from 'next/image';
|
||||
|
||||
export default function AnimalResearch() {
|
||||
return (
|
||||
<div id="animal-research" className="bg-white p-6 rounded-lg shadow-md">
|
||||
{/* Heading (Above Image and Text) */}
|
||||
<h2 className="text-3xl font-bold text-gray-800 mb-4">Animal Research</h2>
|
||||
{/* Flex container for image and text (description paragraphs) */}
|
||||
<div className="flex flex-col md:flex-row gap-6 mb-6">
|
||||
{/* Image Section (Left) */}
|
||||
<div className="flex-shrink-0 md:w-1/3">
|
||||
<Image
|
||||
src="/images/blog/animan_fni.jpg"
|
||||
alt="Animal Research"
|
||||
width={300}
|
||||
height={200}
|
||||
className="object-cover rounded-lg w-full h-48 md:h-full"
|
||||
/>
|
||||
</div>
|
||||
{/* Text Section (Right) */}
|
||||
<div className="flex-1">
|
||||
<p className="text-gray-600 mb-4">
|
||||
Animal genomics plays a vital role in understanding genetic diversity, disease resistance, and the evolutionary relationships of species. Sequencing animal genomes enables researchers to trace ancestry, identify genes responsible for desirable traits, and analyze gene expression patterns across populations. Our NGS-based animal genomics services provide in-depth analysis of genetic diversity, helping researchers and breeders identify genes that contribute to health, behavior, and other important characteristics.
|
||||
</p>
|
||||
<p className="text-gray-600 mb-4">
|
||||
From wildlife conservation to livestock improvement, we offer comprehensive sequencing solutions that support a wide range of applications. These include whole-genome sequencing, exome sequencing, and targeted sequencing of disease-related genes. Our advanced technologies allow you to study genetic variations with unparalleled precision, offering valuable insights into both domesticated and wild animal populations.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{/* Services Table (Row-wise) */}
|
||||
<h3 className="text-lg font-semibold text-gray-800 mt-4 mb-2">Our sequencing services for Animal Research includes:</h3>
|
||||
<table className="w-full border-collapse border border-gray-300">
|
||||
<tbody>
|
||||
<tr className="bg-gray-100">
|
||||
<td className="border border-gray-300 px-3 py-2 text-left font-semibold text-teal-700 w-1/4">DNA Sequencing</td>
|
||||
<td className="border border-gray-300 px-3 py-2 align-top p-2" style={{ color: '#555555' }}>Whole Genome (Short Read, Long Read, Hybrid), Enrichment (Whole Exome, Amplicon and Targeted), Single Cell DNA, Genome Mapping, Genotyping (Based on SNP, ddRAD, Microsatellites)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="border border-gray-300 px-3 py-2 text-left font-semibold text-teal-700 w-1/4">RNA Sequencing</td>
|
||||
<td className="border border-gray-300 px-3 py-2 align-top p-2" style={{ color: '#555555' }}>Total RNA, mRNA, Small RNA, Long Non-Coding, IncRNA, Iso Sequencing</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
43
app/components/Research/HumanResearch.jsx
Normal file
43
app/components/Research/HumanResearch.jsx
Normal file
@ -0,0 +1,43 @@
|
||||
import Image from 'next/image';
|
||||
|
||||
export default function HumanResearch() {
|
||||
return (
|
||||
<div id="human-research" className="bg-white p-6 rounded-lg shadow-md">
|
||||
{/* Heading (Above Image and Text) */}
|
||||
<h2 className="text-3xl font-bold text-gray-800 mb-4">Human Research</h2>
|
||||
{/* Flex container for image and text (description paragraphs) */}
|
||||
<div className="flex flex-col md:flex-row gap-6 mb-6">
|
||||
{/* Image Section (Left) */}
|
||||
<div className="flex-shrink-0 md:w-1/3">
|
||||
<Image
|
||||
src="/images/blog/human_fn.jpg"
|
||||
alt="Human Research"
|
||||
width={300}
|
||||
height={200}
|
||||
className="object-cover rounded-lg w-full h-48 md:h-full"
|
||||
/>
|
||||
</div>
|
||||
{/* Text Section (Right) */}
|
||||
<div className="flex-1">
|
||||
<p className="text-gray-600 mb-4">
|
||||
Human genomics is at the forefront of understanding complex diseases, genetic disorders, and personalized medicine. By sequencing genomes or exomes, researchers can identify genomic alterations that contribute to rare diseases, cancer, and complex disorders such as diabetes or heart disease. Rapid sequencing technologies enable high-resolution insights into genetic variants, allowing for better diagnostics and targeted therapeutic strategies.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{/* Services Table (Row-wise) */}
|
||||
<h3 className="text-lg font-semibold text-gray-800 mt-4 mb-2">Our sequencing services for Human Research includes:</h3>
|
||||
<table className="w-full border-collapse border border-gray-300">
|
||||
<tbody>
|
||||
<tr className="bg-gray-100">
|
||||
<td className="border border-gray-300 px-3 py-2 text-left font-semibold text-teal-700 w-1/4">DNA Sequencing</td>
|
||||
<td className="border border-gray-300 px-3 py-2 align-top p-2" style={{ color: '#555555' }}>Whole Genome (Short Read, Long Read, Hybrid), Enrichment (Whole Exome, Amplicon and Targeted), Single Cell DNA, Genome Mapping, Genotyping (Based on SNP, ddRAD, Microsatellites)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="border border-gray-300 px-3 py-2 text-left font-semibold text-teal-700 w-1/4">RNA Sequencing</td>
|
||||
<td className="border border-gray-300 px-3 py-2 align-top p-2" style={{ color: '#555555' }}>Total RNA, mRNA, Small RNA, Long Non-Coding, IncRNA, Iso Sequencing</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
46
app/components/Research/MicrobialResearch.jsx
Normal file
46
app/components/Research/MicrobialResearch.jsx
Normal file
@ -0,0 +1,46 @@
|
||||
import Image from 'next/image';
|
||||
|
||||
export default function MicrobialResearch() {
|
||||
return (
|
||||
<div id="microbial-research" className="bg-white p-6 rounded-lg shadow-md">
|
||||
{/* Heading (Above Image and Text) */}
|
||||
<h2 className="text-3xl font-bold text-gray-800 mb-4">Microbial Research</h2>
|
||||
{/* Flex container for image and text (description paragraphs) */}
|
||||
<div className="flex flex-col md:flex-row gap-6 mb-6">
|
||||
{/* Image Section (Left) */}
|
||||
<div className="flex-shrink-0 md:w-1/3">
|
||||
<Image
|
||||
src="/images/blog/microbial_fn.jpg"
|
||||
alt="Microbial Research"
|
||||
width={300}
|
||||
height={200}
|
||||
className="object-cover rounded-lg w-full h-48 md:h-full"
|
||||
/>
|
||||
</div>
|
||||
{/* Text Section (Right) */}
|
||||
<div className="flex-1">
|
||||
<p className="text-gray-600 mb-4">
|
||||
Microorganisms are present in almost all environments and play critical roles in industrial fermentation, medical health, and bioengineering. Genomic sequencing provides powerful insights into the genetic makeup of the microbial world, enabling researchers to understand how microbes impact humans and the environment.
|
||||
</p>
|
||||
<p className="text-gray-600 mb-4">
|
||||
We offer a broad array of microbial genomics solutions that help you study bacteria, viruses, fungi, and parasites with high-resolution, culture-independent methods. Whether you're conducting metagenomics studies or tracking disease outbreaks, our next-generation sequencing (NGS) technologies—such as Illumina and PacBio—provide comprehensive insights into microbial communities.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{/* Services Table (Row-wise) */}
|
||||
<h3 className="text-lg font-semibold text-gray-800 mt-4 mb-2">Our sequencing services for Microbial Research includes:</h3>
|
||||
<table className="w-full border-collapse border border-gray-300">
|
||||
<tbody>
|
||||
<tr className="bg-gray-100">
|
||||
<td className="border border-gray-300 px-3 py-2 text-left font-semibold text-teal-700 w-1/4">DNA Sequencing</td>
|
||||
<td className="border border-gray-300 px-3 py-2 align-top p-2" style={{ color: '#555555' }}>Whole Genome, ddRAD, Enrichment, SNP, Single Cell DNA, Microsatellites, Genome Mapping, Metagenomic Shotgun Sequencing, 16s/18s/ITS Amplicon Sequencing, Viral Metagenomic Sequencing</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="border border-gray-300 px-3 py-2 text-left font-semibold text-teal-700 w-1/4">RNA Sequencing</td>
|
||||
<td className="border border-gray-300 px-3 py-2 align-top p-2" style={{ color: '#555555' }}>Total RNA, mRNA, Small RNA, Long Non-Coding, IncRNA, Metatranscriptomics, Iso Sequencing</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
46
app/components/Research/PlantResearch.jsx
Normal file
46
app/components/Research/PlantResearch.jsx
Normal file
@ -0,0 +1,46 @@
|
||||
import Image from 'next/image';
|
||||
|
||||
export default function PlantResearch() {
|
||||
return (
|
||||
<div id="plant-research" className="bg-white p-6 rounded-lg shadow-md">
|
||||
{/* Heading (Above Image and Text) */}
|
||||
<h2 className="text-3xl font-bold text-gray-800 mb-4">Plant Research</h2>
|
||||
{/* Flex container for image and text (description paragraphs) */}
|
||||
<div className="flex flex-col md:flex-row gap-6 mb-6">
|
||||
{/* Image Section (Left) */}
|
||||
<div className="flex-shrink-0 md:w-1/3">
|
||||
<Image
|
||||
src="/images/blog/plant_fni.jpg"
|
||||
alt="Plant Research"
|
||||
width={300}
|
||||
height={200}
|
||||
className="object-cover rounded-lg w-full h-48 md:h-full"
|
||||
/>
|
||||
</div>
|
||||
{/* Text Section (Right) */}
|
||||
<div className="flex-1">
|
||||
<p className="text-gray-600 mb-4">
|
||||
Plants are the foundation of life on Earth, and understanding their genetic makeup is key to improving crop production, disease resistance, and sustainability. Genomic sequencing enables researchers to identify the genes responsible for important traits such as yield, stress tolerance, and disease resistance. Our next-generation sequencing (NGS) solutions provide the tools necessary to generate accurate reference genomes, study genetic variations, and accelerate the discovery of traits that can help address global agricultural challenges.
|
||||
</p>
|
||||
<p className="text-gray-600 mb-4">
|
||||
Whether you're working on crop improvement or exploring the genetic basis of plant resilience, our comprehensive NGS methods provide you with fast, reliable insights into plant genomes. We offer a range of services including whole-genome sequencing, exome sequencing, and targeted sequencing for gene discovery and trait analysis. With our plant genomics solutions, you can improve breeding programs and develop more resilient, high-yielding crops for the future.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{/* Services Table (Row-wise) */}
|
||||
<h3 className="text-lg font-semibold text-gray-800 mt-4 mb-2">Our sequencing services for Plant Research includes:</h3>
|
||||
<table className="w-full border-collapse border border-gray-300">
|
||||
<tbody>
|
||||
<tr className="bg-gray-100">
|
||||
<td className="border border-gray-300 px-3 py-2 text-left font-semibold text-teal-700 w-1/4">DNA Sequencing</td>
|
||||
<td className="border border-gray-300 px-3 py-2 align-top p-2" style={{ color: '#555555' }}>Whole Genome (Short Read, Long Read, Hybrid), Enrichment (Whole Exome, Amplicon and Targeted), Single Cell DNA, Genome Mapping, Genotyping (Based on SNP, ddRAD, Microsatellites)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="border border-gray-300 px-3 py-2 text-left font-semibold text-teal-700 w-1/4">RNA Sequencing</td>
|
||||
<td className="border border-gray-300 px-3 py-2 align-top p-2" style={{ color: '#555555' }}>Total RNA, mRNA, Small RNA, Long Non-Coding, IncRNA, Iso Sequencing</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
35
app/components/Research/ResearchHero.jsx
Normal file
35
app/components/Research/ResearchHero.jsx
Normal file
@ -0,0 +1,35 @@
|
||||
import React from 'react';
|
||||
|
||||
const ResearchHero = () => {
|
||||
return (
|
||||
<section
|
||||
className="relative bg-cover bg-center py-6 h-24"
|
||||
style={{ backgroundImage: "url('images/bredcrumb.jpg')" }}
|
||||
>
|
||||
{/* Breadcrumb */}
|
||||
<div className="relative z-10 mb-1 -mt-3">
|
||||
<div className="container mx-auto max-w-none px-4">
|
||||
<nav className="flex items-center space-x-2 text-sm">
|
||||
<a href="/" className="text-white hover:text-yellow-400 underline">Home</a>
|
||||
<span className="text-white">
|
||||
<svg className="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clipRule="evenodd" />
|
||||
</svg>
|
||||
</span>
|
||||
<span className="text-white">Research Area</span>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Page Title */}
|
||||
<div className="relative z-10 text-center -mt-2">
|
||||
<h1 className="text-4xl md:text-5xl font-bold text-white mb-2">
|
||||
Research Area
|
||||
</h1>
|
||||
<div className="w-16 h-1 bg-yellow-400 mx-auto"></div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default ResearchHero;
|
||||
82
app/components/Research/ResearchSection.jsx
Normal file
82
app/components/Research/ResearchSection.jsx
Normal file
@ -0,0 +1,82 @@
|
||||
import React from 'react';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
|
||||
const ResearchCard = ({ image, title, description, link }) => (
|
||||
<Link href={link} className="block">
|
||||
<div className="flex flex-col sm:flex-row items-start gap-4 p-4 sm:p-6 rounded-2xl sm:rounded-3xl shadow-sm hover:shadow-lg transition-all duration-300 group relative" style={{ backgroundColor: '#f2fcfc' }}>
|
||||
{/* Image Section */}
|
||||
<div className="flex-shrink-0 w-full sm:w-auto">
|
||||
<div className="relative overflow-hidden rounded-xl sm:rounded-2xl">
|
||||
<Image
|
||||
src={image}
|
||||
alt={title}
|
||||
width={180}
|
||||
height={140}
|
||||
className="object-cover w-full h-48 sm:w-44 sm:h-36 group-hover:scale-105 transition-transform duration-300"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content Section */}
|
||||
<div className="flex-1 pt-1 pb-12 sm:pb-12">
|
||||
<h3 className="text-lg sm:text-lg font-semibold text-teal-700 mb-3 leading-tight">{title}</h3>
|
||||
<p className="text-gray-600 leading-relaxed text-sm text-justify pr-4 sm:pr-0">{description}</p>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
|
||||
const ResearchSection = () => {
|
||||
const researchAreas = [
|
||||
{
|
||||
image: "/images/blog/plant_fni.jpg",
|
||||
title: "Plant Research",
|
||||
description: "Enable researchers to generate accurate reference genomes for plants, study genetic variations, and identify genes related to traits like yield, disease resistance, and stress tolerance.",
|
||||
link: "/research#plant-research"
|
||||
},
|
||||
{
|
||||
image: "/images/blog/animan_fni.jpg",
|
||||
title: "Animal Research",
|
||||
description: "Enable researchers to study the genetic diversity of animal populations, trace ancestry, identify disease-resistant genes, and analyze gene expression patterns.",
|
||||
link: "/research#animal-research"
|
||||
},
|
||||
{
|
||||
image: "/images/blog/microbial_fn.jpg",
|
||||
title: "Microbial Research",
|
||||
description: "Enable pathogen identification, outbreak tracking, and studying antimicrobial resistance by sequencing the genomes of bacteria, viruses, and other microbes.",
|
||||
link: "/research#microbial-research"
|
||||
},
|
||||
{
|
||||
image: "/images/blog/human_fn.jpg",
|
||||
title: "Human Research",
|
||||
description: "Enable rapid sequencing of genomes or exomes to identify genomic alterations associated with rare and complex disorders.",
|
||||
link: "/research#human-research"
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<section id="research" className="py-6 sm:py-8 bg-white">
|
||||
<div className="container mx-auto max-w-none px-6">
|
||||
<div className="mb-8">
|
||||
<h2 className="text-4xl font-bold text-teal-700 mb-4">Research Area</h2>
|
||||
</div>
|
||||
|
||||
{/* Grid layout - 1 column on mobile, 2 columns on desktop */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4 sm:gap-6">
|
||||
{researchAreas.map((area, index) => (
|
||||
<ResearchCard
|
||||
key={index}
|
||||
image={area.image}
|
||||
title={area.title}
|
||||
description={area.description}
|
||||
link={area.link}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default ResearchSection;
|
||||
103
app/components/SampleForm/BioinformaticsSection.jsx
Normal file
103
app/components/SampleForm/BioinformaticsSection.jsx
Normal file
@ -0,0 +1,103 @@
|
||||
import React from 'react';
|
||||
|
||||
const BioinformaticsSection = ({ formData, onInputChange }) => {
|
||||
const handleChange = (field, value) => {
|
||||
onInputChange('bioinformatics', field, value);
|
||||
};
|
||||
|
||||
const analysisOptions = [
|
||||
'', 'Variant Calling', 'Differential Expression', 'De Novo Assembly', 'Others'
|
||||
];
|
||||
|
||||
const genomeOptions = [
|
||||
'', 'Yes', 'No', 'Others'
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="bg-white mx-6 p-6 rounded-lg">
|
||||
<h2 className="text-teal-600 text-lg font-bold mb-6 border-b-2 border-teal-600 pb-1">
|
||||
Bioinformatics Information
|
||||
</h2>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block mb-2 font-semibold text-sm text-gray-600">
|
||||
Analysis Requested <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<select
|
||||
value={formData.Analysis_Requested}
|
||||
onChange={(e) => handleChange('Analysis_Requested', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-sm font-arial h-10 box-border"
|
||||
required
|
||||
>
|
||||
{analysisOptions.map(option => (
|
||||
<option key={option} value={option} style={{ color: '#555555' }}>
|
||||
{option || 'Select'}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block mb-2 font-semibold text-sm text-gray-600">Please Specify Details</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.Analysis_Details}
|
||||
onChange={(e) => handleChange('Analysis_Details', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-sm font-arial h-10 box-border"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block mb-2 font-semibold text-sm text-gray-600">
|
||||
Reference Genome Available <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<select
|
||||
value={formData.Reference_Genome_Available}
|
||||
onChange={(e) => handleChange('Reference_Genome_Available', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-sm font-arial h-10 box-border"
|
||||
required
|
||||
>
|
||||
{genomeOptions.map(option => (
|
||||
<option key={option} value={option} style={{ color: '#555555' }}>
|
||||
{option || 'Select'}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block mb-2 font-semibold text-sm text-gray-600">If Yes, Please Mention Genome Size</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.Genome_Size}
|
||||
onChange={(e) => handleChange('Genome_Size', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-sm font-arial h-10 box-border"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block mb-2 font-semibold text-sm text-gray-600">
|
||||
Special Consideration <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<textarea
|
||||
value={formData.Special_Consideration}
|
||||
onChange={(e) => handleChange('Special_Consideration', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-sm font-arial box-border"
|
||||
rows={4}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default BioinformaticsSection;
|
||||
179
app/components/SampleForm/CustomerInfoSection.jsx
Normal file
179
app/components/SampleForm/CustomerInfoSection.jsx
Normal file
@ -0,0 +1,179 @@
|
||||
import React from 'react';
|
||||
|
||||
const CustomerInfoSection = ({ formData, onInputChange }) => {
|
||||
const handleChange = (field, value) => {
|
||||
onInputChange('customer', field, value);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-white mx-6 mt-6 p-6 rounded-lg">
|
||||
<h2 className="text-teal-600 text-lg font-semibold mb-6 border-b-2 border-teal-600 pb-1">
|
||||
Customer Information
|
||||
</h2>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block mb-2 font-semibold text-sm text-gray-600">
|
||||
Principal Investigator <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.Principal_Investigator}
|
||||
onChange={(e) => handleChange('Principal_Investigator', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-sm font-arial h-10 box-border"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block mb-2 font-semibold text-sm text-gray-600">
|
||||
Email <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
value={formData.Email}
|
||||
onChange={(e) => handleChange('Email', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-sm font-arial h-10 box-border"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block mb-2 font-semibold text-sm text-gray-600">
|
||||
Company/Institution <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.Company_Institution}
|
||||
onChange={(e) => handleChange('Company_Institution', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-sm font-arial h-10 box-border"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block mb-2 font-semibold text-sm text-gray-600">
|
||||
Contact Number <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="tel"
|
||||
value={formData.Contact_Number}
|
||||
onChange={(e) => handleChange('Contact_Number', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-sm font-arial h-10 box-border"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block mb-2 font-semibold text-sm text-gray-600">
|
||||
Address <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.Address}
|
||||
onChange={(e) => handleChange('Address', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-sm font-arial h-10 box-border"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div>
|
||||
<label className="block mb-2 font-semibold text-sm text-gray-600">
|
||||
City <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.City}
|
||||
onChange={(e) => handleChange('City', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-sm font-arial h-10 box-border"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block mb-2 font-semibold text-sm text-gray-600">
|
||||
State <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.State}
|
||||
onChange={(e) => handleChange('State', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-sm font-arial h-10 box-border"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block mb-2 font-semibold text-sm text-gray-600">
|
||||
Pin <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.Pin}
|
||||
onChange={(e) => handleChange('Pin', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-sm font-arial h-10 box-border"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block mb-2 font-semibold text-sm text-gray-600">Secondary Contact</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.Secondary_Contact}
|
||||
onChange={(e) => handleChange('Secondary_Contact', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-sm font-arial h-10 box-border"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block mb-2 font-semibold text-sm text-gray-600">Email</label>
|
||||
<input
|
||||
type="email"
|
||||
value={formData.Secondary_Email}
|
||||
onChange={(e) => handleChange('Secondary_Email', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-sm font-arial h-10 box-border"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block mb-2 font-semibold text-sm text-gray-600">Company/Institution</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.Secondary_Company_Institution}
|
||||
onChange={(e) => handleChange('Secondary_Company_Institution', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-sm font-arial h-10 box-border"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block mb-2 font-semibold text-sm text-gray-600">Contact Number</label>
|
||||
<input
|
||||
type="tel"
|
||||
value={formData.Secondary_Contact_Number}
|
||||
onChange={(e) => handleChange('Secondary_Contact_Number', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-sm font-arial h-10 box-border"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CustomerInfoSection;
|
||||
41
app/components/SampleForm/PageTitle.jsx
Normal file
41
app/components/SampleForm/PageTitle.jsx
Normal file
@ -0,0 +1,41 @@
|
||||
import React from 'react';
|
||||
|
||||
const PageTitle = () => {
|
||||
return (
|
||||
<section
|
||||
className="relative bg-cover bg-center py-4 sm:py-6 h-auto sm:h-32 md:h-40 lg:h-24 min-h-[120px] sm:min-h-[140px]"
|
||||
style={{ backgroundImage: "url('images/bredcrumb.jpg')" }}
|
||||
>
|
||||
{/* Breadcrumb */}
|
||||
<div className="relative z-10 mb-2 sm:mb-1 pt-2 sm:pt-0 sm:-mt-3 lg:-mt-3">
|
||||
<div className="container mx-auto max-w-none px-4">
|
||||
<nav className="flex flex-wrap items-center gap-1 sm:gap-2 text-xs sm:text-sm lg:text-sm">
|
||||
<a href="/" className="text-white hover:text-yellow-400 underline whitespace-nowrap">Home</a>
|
||||
<span className="text-white flex-shrink-0">
|
||||
<svg className="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clipRule="evenodd" />
|
||||
</svg>
|
||||
</span>
|
||||
<a href="/sample-submission-guideline" className="text-white hover:text-yellow-400 underline whitespace-nowrap">Knowledge Hub</a>
|
||||
<span className="text-white flex-shrink-0">
|
||||
<svg className="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clipRule="evenodd" />
|
||||
</svg>
|
||||
</span>
|
||||
<span className="text-white">Sample Initiation Form</span>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Page Title */}
|
||||
<div className="relative z-10 text-center pb-2 sm:pb-0 sm:-mt-2 lg:mt-4">
|
||||
<h1 className="text-lg sm:text-2xl md:text-3xl lg:text-4xl xl:text-4xl font-bold text-white mb-2 px-4 leading-tight">
|
||||
Sample Initiation Form
|
||||
</h1>
|
||||
<div className="w-12 sm:w-14 md:w-16 lg:w-16 h-1 bg-yellow-400 mx-auto"></div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default PageTitle;
|
||||
246
app/components/SampleForm/SampleDetailsSection.jsx
Normal file
246
app/components/SampleForm/SampleDetailsSection.jsx
Normal file
@ -0,0 +1,246 @@
|
||||
'use client';
|
||||
import React from 'react';
|
||||
|
||||
const SampleDetailsSection = ({ sampleDetails, setSampleDetails }) => {
|
||||
const preservativeOptions = [
|
||||
'', 'Trizol', 'RNA later', 'Ethanol', 'Formalin', 'FFPE', 'Others'
|
||||
];
|
||||
|
||||
const tempOptions = [
|
||||
'', 'Ambient', 'Cool pack', 'Dry ice', 'Others'
|
||||
];
|
||||
|
||||
const handleSampleChange = (index, field, value) => {
|
||||
const newSampleDetails = [...sampleDetails];
|
||||
newSampleDetails[index] = {
|
||||
...newSampleDetails[index],
|
||||
[field]: value
|
||||
};
|
||||
setSampleDetails(newSampleDetails);
|
||||
};
|
||||
|
||||
const addRow = () => {
|
||||
setSampleDetails([...sampleDetails, {
|
||||
Serial_Number: '',
|
||||
Sample_Name: '',
|
||||
Storage_Temp: '',
|
||||
Preservative_Reagent: '',
|
||||
Temp_Information: '',
|
||||
Comments: ''
|
||||
}]);
|
||||
};
|
||||
|
||||
const removeRow = (index) => {
|
||||
if (sampleDetails.length > 1) {
|
||||
const newSampleDetails = sampleDetails.filter((_, i) => i !== index);
|
||||
setSampleDetails(newSampleDetails);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-white mx-6 p-6 rounded-lg">
|
||||
<h2 className="text-teal-600 text-lg font-bold mb-6 border-b-2 border-teal-600 pb-1">
|
||||
Sample Details
|
||||
</h2>
|
||||
|
||||
{/* Desktop Table View */}
|
||||
<div className="hidden md:block overflow-x-auto">
|
||||
<table className="w-full border-collapse text-xs">
|
||||
<thead>
|
||||
<tr className="bg-gray-100">
|
||||
<th className="border border-gray-300 p-2 text-left font-bold">Serial Number</th>
|
||||
<th className="border border-gray-300 p-2 text-left font-bold">Sample Name</th>
|
||||
<th className="border border-gray-300 p-2 text-left font-bold">Storage Temp (in °C)</th>
|
||||
<th className="border border-gray-300 p-2 text-left font-bold">Preservative Reagent</th>
|
||||
<th className="border border-gray-300 p-2 text-left font-bold">Temp Information</th>
|
||||
<th className="border border-gray-300 p-2 text-left font-bold">Comments</th>
|
||||
<th className="border border-gray-300 p-2 text-center font-bold w-12"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{sampleDetails.map((sample, index) => (
|
||||
<tr key={index}>
|
||||
<td className="border border-gray-300 p-2">
|
||||
<input
|
||||
type="text"
|
||||
value={sample.Serial_Number}
|
||||
onChange={(e) => handleSampleChange(index, 'Serial_Number', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full text-xs h-8 p-1 border-none outline-none"
|
||||
/>
|
||||
</td>
|
||||
<td className="border border-gray-300 p-2">
|
||||
<input
|
||||
type="text"
|
||||
value={sample.Sample_Name}
|
||||
onChange={(e) => handleSampleChange(index, 'Sample_Name', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full text-xs h-8 p-1 border-none outline-none"
|
||||
/>
|
||||
</td>
|
||||
<td className="border border-gray-300 p-2">
|
||||
<input
|
||||
type="text"
|
||||
value={sample.Storage_Temp}
|
||||
onChange={(e) => handleSampleChange(index, 'Storage_Temp', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full text-xs h-8 p-1 border-none outline-none"
|
||||
/>
|
||||
</td>
|
||||
<td className="border border-gray-300 p-2">
|
||||
<select
|
||||
value={sample.Preservative_Reagent}
|
||||
onChange={(e) => handleSampleChange(index, 'Preservative_Reagent', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full text-xs h-8 p-1 border-none outline-none"
|
||||
>
|
||||
{preservativeOptions.map(option => (
|
||||
<option key={option} value={option} style={{ color: '#555555' }}>
|
||||
{option || 'Select'}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</td>
|
||||
<td className="border border-gray-300 p-2">
|
||||
<select
|
||||
value={sample.Temp_Information}
|
||||
onChange={(e) => handleSampleChange(index, 'Temp_Information', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full text-xs h-8 p-1 border-none outline-none"
|
||||
>
|
||||
{tempOptions.map(option => (
|
||||
<option key={option} value={option} style={{ color: '#555555' }}>
|
||||
{option || 'Select'}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</td>
|
||||
<td className="border border-gray-300 p-2">
|
||||
<input
|
||||
type="text"
|
||||
value={sample.Comments}
|
||||
onChange={(e) => handleSampleChange(index, 'Comments', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full text-xs h-8 p-1 border-none outline-none"
|
||||
/>
|
||||
</td>
|
||||
<td className="border border-gray-300 p-2 text-center">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => removeRow(index)}
|
||||
className="text-gray-400 hover:text-red-500 w-8 h-8 rounded-full flex items-center justify-center transition-colors"
|
||||
disabled={sampleDetails.length === 1}
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{/* Mobile Card View */}
|
||||
<div className="md:hidden space-y-4">
|
||||
{sampleDetails.map((sample, index) => (
|
||||
<div key={index} className="border border-gray-300 rounded p-4 bg-white">
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<label className="block text-xs font-bold text-teal-600 mb-1">Serial Number</label>
|
||||
<input
|
||||
type="text"
|
||||
value={sample.Serial_Number}
|
||||
onChange={(e) => handleSampleChange(index, 'Serial_Number', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-xs h-8"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-xs font-bold text-teal-600 mb-1">Sample Name</label>
|
||||
<input
|
||||
type="text"
|
||||
value={sample.Sample_Name}
|
||||
onChange={(e) => handleSampleChange(index, 'Sample_Name', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-xs h-8"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-xs font-bold text-teal-600 mb-1">Storage Temp (in °C)</label>
|
||||
<input
|
||||
type="text"
|
||||
value={sample.Storage_Temp}
|
||||
onChange={(e) => handleSampleChange(index, 'Storage_Temp', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-xs h-8"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-xs font-bold text-teal-600 mb-1">Preservative Reagent</label>
|
||||
<select
|
||||
value={sample.Preservative_Reagent}
|
||||
onChange={(e) => handleSampleChange(index, 'Preservative_Reagent', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-xs h-8"
|
||||
>
|
||||
{preservativeOptions.map(option => (
|
||||
<option key={option} value={option} style={{ color: '#555555' }}>
|
||||
{option || 'Select'}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-xs font-bold text-teal-600 mb-1">Temp Information</label>
|
||||
<select
|
||||
value={sample.Temp_Information}
|
||||
onChange={(e) => handleSampleChange(index, 'Temp_Information', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-xs h-8"
|
||||
>
|
||||
{tempOptions.map(option => (
|
||||
<option key={option} value={option} style={{ color: '#555555' }}>
|
||||
{option || 'Select'}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-xs font-bold text-teal-600 mb-1">Comments</label>
|
||||
<input
|
||||
type="text"
|
||||
value={sample.Comments}
|
||||
onChange={(e) => handleSampleChange(index, 'Comments', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-xs h-8"
|
||||
/>
|
||||
</div>
|
||||
<div className="text-center mt-4">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => removeRow(index)}
|
||||
className="text-gray-400 hover:text-red-500 w-8 h-8 rounded-full flex items-center justify-center transition-colors mx-auto"
|
||||
disabled={sampleDetails.length === 1}
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Add Row Button */}
|
||||
<button
|
||||
type="button"
|
||||
onClick={addRow}
|
||||
className="mt-4 bg-green-500 hover:bg-green-600 text-white px-4 py-2 rounded text-xs transition-colors md:float-right"
|
||||
>
|
||||
Add Row
|
||||
</button>
|
||||
<div className="clear-both"></div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SampleDetailsSection;
|
||||
297
app/components/SampleForm/SampleFormContainer.jsx
Normal file
297
app/components/SampleForm/SampleFormContainer.jsx
Normal file
@ -0,0 +1,297 @@
|
||||
'use client';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import CustomerInfoSection from './CustomerInfoSection';
|
||||
import SampleInfoSection from './SampleInfoSection';
|
||||
import ServiceInfoSection from './ServiceInfoSection';
|
||||
import BioinformaticsSection from './BioinformaticsSection';
|
||||
import SampleDetailsSection from './SampleDetailsSection';
|
||||
|
||||
const SampleFormContainer = () => {
|
||||
const [formData, setFormData] = useState({
|
||||
// Customer Information
|
||||
Principal_Investigator: '',
|
||||
Email: '',
|
||||
Company_Institution: '',
|
||||
Contact_Number: '',
|
||||
Address: '',
|
||||
City: '',
|
||||
State: '',
|
||||
Pin: '',
|
||||
Secondary_Contact: '',
|
||||
Secondary_Email: '',
|
||||
Secondary_Company_Institution: '',
|
||||
Secondary_Contact_Number: '',
|
||||
|
||||
// Sample Information
|
||||
Project_Title: '',
|
||||
Number_of_Samples: '',
|
||||
Sample_Type: '',
|
||||
Sample_Type_Other: '',
|
||||
Sample_Source: '',
|
||||
Sample_Source_Other: '',
|
||||
Pathogenicity: '',
|
||||
Sample_Remarks: '',
|
||||
|
||||
// Service Information
|
||||
Service_Requested: '',
|
||||
Service_Requested_Other: '',
|
||||
Type_of_Library: '',
|
||||
Type_of_Library_Other: '',
|
||||
Required_Library_Size: '',
|
||||
Required_Library_Size_Other: '',
|
||||
Index_Information: '',
|
||||
Kit_Information: '',
|
||||
Sequencing_Platform: '',
|
||||
Sequencing_Platform_Other: '',
|
||||
Sequencing_Read_Length: '',
|
||||
Sequencing_Read_Length_Other: '',
|
||||
Total_Data_Requirement: '',
|
||||
Service_Remarks: '',
|
||||
|
||||
// Bioinformatics Information
|
||||
Analysis_Requested: '',
|
||||
Analysis_Details: '',
|
||||
Reference_Genome_Available: '',
|
||||
Genome_Size: '',
|
||||
Special_Consideration: '',
|
||||
});
|
||||
|
||||
const [sampleDetails, setSampleDetails] = useState([
|
||||
{
|
||||
Serial_Number: '',
|
||||
Sample_Name: '',
|
||||
Storage_Temp: '',
|
||||
Preservative_Reagent: '',
|
||||
Temp_Information: '',
|
||||
Comments: ''
|
||||
}
|
||||
]);
|
||||
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [message, setMessage] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
// Check for Excel data in sessionStorage
|
||||
const excelData = sessionStorage.getItem('excelData');
|
||||
const uploadedFileName = sessionStorage.getItem('uploadedFileName');
|
||||
|
||||
if (excelData) {
|
||||
try {
|
||||
const jsonData = JSON.parse(excelData);
|
||||
autoFillForm(jsonData);
|
||||
|
||||
// Clear stored data
|
||||
sessionStorage.removeItem('excelData');
|
||||
sessionStorage.removeItem('uploadedFileName');
|
||||
|
||||
setMessage(`Form auto-filled from uploaded file: ${uploadedFileName}`);
|
||||
} catch (error) {
|
||||
console.error('Error parsing Excel data:', error);
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
const autoFillForm = (jsonData) => {
|
||||
if (jsonData.length === 0) return;
|
||||
|
||||
const data = jsonData[0];
|
||||
const newFormData = { ...formData };
|
||||
|
||||
// Helper function to safely get value
|
||||
const getValue = (key) => data[key] ? data[key].toString().trim() : '';
|
||||
|
||||
// Customer Information
|
||||
newFormData.Principal_Investigator = getValue('Principal Investigator');
|
||||
newFormData.Email = getValue('Email');
|
||||
newFormData.Company_Institution = getValue('Company/Institution');
|
||||
newFormData.Contact_Number = getValue('Contact Number');
|
||||
newFormData.Address = getValue('Address');
|
||||
newFormData.City = getValue('City');
|
||||
newFormData.State = getValue('State');
|
||||
newFormData.Pin = getValue('Pin');
|
||||
newFormData.Secondary_Contact = getValue('Secondary Contact');
|
||||
newFormData.Secondary_Email = getValue('Secondary Email');
|
||||
newFormData.Secondary_Company_Institution = getValue('Secondary Company/Institution');
|
||||
newFormData.Secondary_Contact_Number = getValue('Secondary Contact Number');
|
||||
|
||||
// Sample Information
|
||||
newFormData.Project_Title = getValue('Project Title');
|
||||
newFormData.Number_of_Samples = getValue('Number of Samples');
|
||||
newFormData.Sample_Type = getValue('Sample Type');
|
||||
newFormData.Sample_Type_Other = getValue('Sample Type Other');
|
||||
newFormData.Sample_Source = getValue('Sample Source');
|
||||
newFormData.Sample_Source_Other = getValue('Sample Source Other');
|
||||
newFormData.Pathogenicity = getValue('Pathogenicity');
|
||||
newFormData.Sample_Remarks = getValue('Sample Remarks');
|
||||
|
||||
// Service Information
|
||||
newFormData.Service_Requested = getValue('Service Requested');
|
||||
newFormData.Service_Requested_Other = getValue('Service Requested Other');
|
||||
newFormData.Type_of_Library = getValue('Type of Library');
|
||||
newFormData.Type_of_Library_Other = getValue('Type of Library Other');
|
||||
newFormData.Required_Library_Size = getValue('Required Library Size');
|
||||
newFormData.Required_Library_Size_Other = getValue('Required Library Size Other');
|
||||
newFormData.Index_Information = getValue('Index Information');
|
||||
newFormData.Kit_Information = getValue('Kit Information');
|
||||
newFormData.Sequencing_Platform = getValue('Sequencing Platform');
|
||||
newFormData.Sequencing_Platform_Other = getValue('Sequencing Platform Other');
|
||||
newFormData.Sequencing_Read_Length = getValue('Sequencing Read Length');
|
||||
newFormData.Sequencing_Read_Length_Other = getValue('Sequencing Read Length Other');
|
||||
newFormData.Total_Data_Requirement = getValue('Total Data Requirement');
|
||||
newFormData.Service_Remarks = getValue('Service Remarks');
|
||||
|
||||
// Bioinformatics Information
|
||||
newFormData.Analysis_Requested = getValue('Analysis Requested');
|
||||
newFormData.Analysis_Details = getValue('Analysis Details');
|
||||
newFormData.Reference_Genome_Available = getValue('Reference Genome Available');
|
||||
newFormData.Genome_Size = getValue('Genome Size');
|
||||
newFormData.Special_Consideration = getValue('Special Consideration');
|
||||
|
||||
setFormData(newFormData);
|
||||
|
||||
// Handle Sample Details
|
||||
const sampleDetailsData = jsonData.filter(row =>
|
||||
row['Serial Number'] || row['Sample Name'] ||
|
||||
row['Storage Temp'] || row['Preservative Reagent'] ||
|
||||
row['Temp Information'] || row['Comments']
|
||||
);
|
||||
|
||||
if (sampleDetailsData.length > 0) {
|
||||
const newSampleDetails = sampleDetailsData.map(sample => ({
|
||||
Serial_Number: getValue('Serial Number'),
|
||||
Sample_Name: getValue('Sample Name'),
|
||||
Storage_Temp: getValue('Storage Temp'),
|
||||
Preservative_Reagent: getValue('Preservative Reagent'),
|
||||
Temp_Information: getValue('Temp Information'),
|
||||
Comments: getValue('Comments')
|
||||
}));
|
||||
setSampleDetails(newSampleDetails);
|
||||
}
|
||||
};
|
||||
|
||||
const handleInputChange = (section, field, value) => {
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
[field]: value
|
||||
}));
|
||||
};
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
setIsSubmitting(true);
|
||||
setMessage(''); // Clear previous messages
|
||||
|
||||
try {
|
||||
const formDataToSend = new FormData();
|
||||
|
||||
// Add form data
|
||||
Object.keys(formData).forEach(key => {
|
||||
if (formData[key]) {
|
||||
formDataToSend.append(key, formData[key]);
|
||||
}
|
||||
});
|
||||
|
||||
// Add sample details as JSON string
|
||||
formDataToSend.append('sample_details', JSON.stringify(sampleDetails));
|
||||
formDataToSend.append('form_type', 'sample');
|
||||
|
||||
console.log('Submitting form data:', formData);
|
||||
console.log('Sample details:', sampleDetails);
|
||||
|
||||
const response = await fetch('/api/forms', {
|
||||
method: 'POST',
|
||||
body: formDataToSend,
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
console.log('API Response:', result);
|
||||
|
||||
if (response.ok) {
|
||||
setMessage(result.message);
|
||||
|
||||
// Reset form after successful submission
|
||||
setFormData({
|
||||
Principal_Investigator: '', Email: '', Company_Institution: '', Contact_Number: '',
|
||||
Address: '', City: '', State: '', Pin: '', Secondary_Contact: '', Secondary_Email: '',
|
||||
Secondary_Company_Institution: '', Secondary_Contact_Number: '', Project_Title: '',
|
||||
Number_of_Samples: '', Sample_Type: '', Sample_Type_Other: '', Sample_Source: '',
|
||||
Sample_Source_Other: '', Pathogenicity: '', Sample_Remarks: '', Service_Requested: '',
|
||||
Service_Requested_Other: '', Type_of_Library: '', Type_of_Library_Other: '',
|
||||
Required_Library_Size: '', Required_Library_Size_Other: '', Index_Information: '',
|
||||
Kit_Information: '', Sequencing_Platform: '', Sequencing_Platform_Other: '',
|
||||
Sequencing_Read_Length: '', Sequencing_Read_Length_Other: '', Total_Data_Requirement: '',
|
||||
Service_Remarks: '', Analysis_Requested: '', Analysis_Details: '',
|
||||
Reference_Genome_Available: '', Genome_Size: '', Special_Consideration: ''
|
||||
});
|
||||
|
||||
setSampleDetails([{
|
||||
Serial_Number: '', Sample_Name: '', Storage_Temp: '',
|
||||
Preservative_Reagent: '', Temp_Information: '', Comments: ''
|
||||
}]);
|
||||
} else {
|
||||
setMessage('Error: ' + (result.error || 'Form submission failed'));
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error submitting form:', error);
|
||||
setMessage('Error: Network error. Please check your connection and try again.');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-teal-50 min-h-screen py-8">
|
||||
<div className="max-w-4xl mx-auto bg-teal-50 shadow-lg border border-gray-300 font-arial text-xs">
|
||||
<form onSubmit={handleSubmit} className="space-y-6">
|
||||
{/* Show message if exists */}
|
||||
{message && (
|
||||
<div className="mx-6 mt-6">
|
||||
<div className={`p-4 rounded ${message.includes('Error') ? 'bg-red-100 text-red-800' : 'bg-green-100 text-green-800'}`}>
|
||||
{message}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<CustomerInfoSection
|
||||
formData={formData}
|
||||
onInputChange={handleInputChange}
|
||||
/>
|
||||
|
||||
<SampleInfoSection
|
||||
formData={formData}
|
||||
onInputChange={handleInputChange}
|
||||
/>
|
||||
|
||||
<ServiceInfoSection
|
||||
formData={formData}
|
||||
onInputChange={handleInputChange}
|
||||
/>
|
||||
|
||||
<BioinformaticsSection
|
||||
formData={formData}
|
||||
onInputChange={handleInputChange}
|
||||
/>
|
||||
|
||||
<SampleDetailsSection
|
||||
sampleDetails={sampleDetails}
|
||||
setSampleDetails={setSampleDetails}
|
||||
/>
|
||||
|
||||
{/* Submit Button */}
|
||||
<div className="text-center py-6">
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isSubmitting}
|
||||
className="bg-teal-600 hover:bg-teal-700 text-white font-medium py-3 px-6 rounded disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||||
>
|
||||
{isSubmitting ? 'Submitting...' : 'Submit Sample Form'}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SampleFormContainer;
|
||||
14
app/components/SampleForm/SampleFormPage.jsx
Normal file
14
app/components/SampleForm/SampleFormPage.jsx
Normal file
@ -0,0 +1,14 @@
|
||||
import React from 'react';
|
||||
import PageTitle from './PageTitle';
|
||||
import SampleFormContainer from './SampleFormContainer';
|
||||
|
||||
const SampleFormPage = () => {
|
||||
return (
|
||||
<div className="page-content">
|
||||
<PageTitle />
|
||||
<SampleFormContainer />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SampleFormPage;
|
||||
144
app/components/SampleForm/SampleInfoSection.jsx
Normal file
144
app/components/SampleForm/SampleInfoSection.jsx
Normal file
@ -0,0 +1,144 @@
|
||||
import React from 'react';
|
||||
|
||||
const SampleInfoSection = ({ formData, onInputChange }) => {
|
||||
const handleChange = (field, value) => {
|
||||
onInputChange('sample', field, value);
|
||||
};
|
||||
|
||||
const sampleTypeOptions = [
|
||||
'', 'DNA', 'RNA', 'cfDNA', 'Blood', 'Saliva', 'Swabs', 'Bodily Fluids', 'Feaces', 'Soil', 'Seeds', 'Water',
|
||||
'Fresh/Frozen tissue', 'FFPE block', 'Plant Tissue', 'Animal Tissue', 'Ready to Run Library (RTRL)', 'Others'
|
||||
];
|
||||
|
||||
const sampleSourceOptions = [
|
||||
'', 'Plant', 'Human', 'Microbial', 'Animal', 'Environmental', 'Others'
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="bg-white mx-6 p-6 rounded-lg">
|
||||
<h2 className="text-teal-600 text-lg font-bold mb-6 border-b-2 border-teal-600 pb-1">
|
||||
Sample Information
|
||||
</h2>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block mb-2 font-semibold text-sm text-gray-600">
|
||||
Project Title <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.Project_Title}
|
||||
onChange={(e) => handleChange('Project_Title', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-sm font-arial h-10 box-border"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block mb-2 font-semibold text-sm text-gray-600">
|
||||
Number of Samples <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.Number_of_Samples}
|
||||
onChange={(e) => handleChange('Number_of_Samples', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-sm font-arial h-10 box-border"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block mb-2 font-semibold text-sm text-gray-600">
|
||||
Sample Type <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<select
|
||||
value={formData.Sample_Type}
|
||||
onChange={(e) => handleChange('Sample_Type', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-sm font-arial h-10 box-border"
|
||||
required
|
||||
>
|
||||
{sampleTypeOptions.map(option => (
|
||||
<option key={option} value={option} style={{ color: '#555555' }}>
|
||||
{option || 'Select'}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block mb-2 font-semibold text-sm text-gray-600">Please Specify (if Others)</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.Sample_Type_Other}
|
||||
onChange={(e) => handleChange('Sample_Type_Other', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-sm font-arial h-10 box-border"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block mb-2 font-semibold text-sm text-gray-600">
|
||||
Sample Source <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<select
|
||||
value={formData.Sample_Source}
|
||||
onChange={(e) => handleChange('Sample_Source', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-sm font-arial h-10 box-border"
|
||||
required
|
||||
>
|
||||
{sampleSourceOptions.map(option => (
|
||||
<option key={option} value={option} style={{ color: '#555555' }}>
|
||||
{option || 'Select'}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block mb-2 font-semibold text-sm text-gray-600">Please Specify (if Others)</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.Sample_Source_Other}
|
||||
onChange={(e) => handleChange('Sample_Source_Other', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-sm font-arial h-10 box-border"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block mb-2 font-semibold text-sm text-gray-600">
|
||||
Pathogenicity <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.Pathogenicity}
|
||||
onChange={(e) => handleChange('Pathogenicity', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-sm font-arial h-10 box-border"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block mb-2 font-semibold text-sm text-gray-600">Remarks</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.Sample_Remarks}
|
||||
onChange={(e) => handleChange('Sample_Remarks', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-sm font-arial h-10 box-border"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SampleInfoSection;
|
||||
266
app/components/SampleForm/ServiceInfoSection.jsx
Normal file
266
app/components/SampleForm/ServiceInfoSection.jsx
Normal file
@ -0,0 +1,266 @@
|
||||
import React from 'react';
|
||||
|
||||
const ServiceInfoSection = ({ formData, onInputChange }) => {
|
||||
const handleChange = (field, value) => {
|
||||
onInputChange('service', field, value);
|
||||
};
|
||||
|
||||
const serviceOptions = [
|
||||
'', 'DNA Sequencing - Whole Genome', 'DNA Sequencing - Whole Exome DNA Sequencing',
|
||||
'DNA Sequencing - Amplicon Sequencing (16S/18S/ITS)', 'DNA Sequencing - Targeted Sequencing',
|
||||
'DNA Sequencing - Single Cell DNA', 'DNA Sequencing - Microbiome and Metagenomics',
|
||||
'DNA Sequencing - Whole Genome Bisulphite', 'DNA Sequencing - ChIP Sequencing',
|
||||
'DNA Sequencing - ATAC Sequencing', 'DNA Sequencing - HiC Sequencing',
|
||||
'DNA Sequencing - Optical Mapping', 'DNA Sequencing - Long Read Sequencing by Pacbio',
|
||||
'DNA Sequencing - Long Read Sequencing by Nanopore', 'DNA Sequencing - Hybrid',
|
||||
'RNA Sequencing- Total RNA', 'RNA Sequencing- mRNA', 'RNA Sequencing- Small RNA',
|
||||
'RNA Sequencing- Long Non-Coding', 'RNA Sequencing- lncRNA', 'RNA Sequencing- Metatranscriptomics',
|
||||
'RNA Sequencing- Degradome', 'RNA Sequencing- Iso Sequencing', 'RNA Sequencing- Circular RNA',
|
||||
'RNA Sequencing- Single Cell RNA', 'Genotyping- ddRAD', 'Genotyping- SNP',
|
||||
'Genotyping- Microsatellites/ STR/SSR/FLA', 'Genotyping- Sequencing based',
|
||||
'Genotyping- GWAS', 'Genotyping- DNA Fingerprinting', 'Others'
|
||||
];
|
||||
|
||||
const libraryTypeOptions = [
|
||||
'', 'Single-End Sequencing', 'Paired-End Sequencing', 'Mate-Pair Sequencing',
|
||||
'Continuous Long Read (CLR)', 'High-Fidelity (HiFi)',
|
||||
'High-throughput Chromosome Conformation Capture (HiC)', 'Optical Mapping', 'Others'
|
||||
];
|
||||
|
||||
const librarySizeOptions = [
|
||||
'', '250 bp (Illumina)', '300 bp (Illumina)', '450 bp (Illumina)', '550 bp (Illumina)',
|
||||
'800 bp (Illumina)', '1 kb (Oxford Nanopore Technologies, ONT)',
|
||||
'5 kb (Oxford Nanopore Technologies, ONT)', '7 kb (Oxford Nanopore Technologies, ONT)',
|
||||
'10 kb (Oxford Nanopore Technologies, ONT)', '1-2 kb (PacBio)', '2-3 kb (PacBio)',
|
||||
'3-6 kb (PacBio)', '5-10 kb (PacBio)', '10 kb (Continuous Long Read, CLR)',
|
||||
'20 kb CLR (Continuous Long Read, CLR)', 'Others'
|
||||
];
|
||||
|
||||
const platformOptions = [
|
||||
'', 'NovaSeq 6000/ NovaSeq X', 'NextSeq', 'MiSeq', 'Pacific Biosciences(PacBio)',
|
||||
'Oxford Nanopore Technologies (ONT)', 'NanoString', 'Saphyr System (Optical Mapping)',
|
||||
'Illumina iScan System', 'Kompetitive Allele-Specific PCR (KASPAR)', 'Others'
|
||||
];
|
||||
|
||||
const readLengthOptions = [
|
||||
'', '1X50bp (Illumina)', '1X100bp (Illumina)', '2X100bp (Illumina)',
|
||||
'2X150bp (Illumina)', '2X250bp (Illumina)', '2X125bp (Illumina)',
|
||||
'2X300bp (Illumina)', 'Others'
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="bg-white mx-6 p-6 rounded-lg">
|
||||
<h2 className="text-teal-600 text-lg font-bold mb-6 border-b-2 border-teal-600 pb-1">
|
||||
Service Information
|
||||
</h2>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block mb-2 font-semibold text-sm text-gray-600">
|
||||
Service Requested <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<select
|
||||
value={formData.Service_Requested}
|
||||
onChange={(e) => handleChange('Service_Requested', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-sm font-arial h-10 box-border"
|
||||
required
|
||||
>
|
||||
{serviceOptions.map(option => (
|
||||
<option key={option} value={option} style={{ color: '#555555' }}>
|
||||
{option || 'Select'}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block mb-2 font-semibold text-sm text-gray-600">Please Specify (if Others)</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.Service_Requested_Other}
|
||||
onChange={(e) => handleChange('Service_Requested_Other', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-sm font-arial h-10 box-border"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block mb-2 font-semibold text-sm text-gray-600">
|
||||
Type of Library <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<select
|
||||
value={formData.Type_of_Library}
|
||||
onChange={(e) => handleChange('Type_of_Library', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-sm font-arial h-10 box-border"
|
||||
required
|
||||
>
|
||||
{libraryTypeOptions.map(option => (
|
||||
<option key={option} value={option} style={{ color: '#555555' }}>
|
||||
{option || 'Select'}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block mb-2 font-semibold text-sm text-gray-600">Please Specify (if Others)</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.Type_of_Library_Other}
|
||||
onChange={(e) => handleChange('Type_of_Library_Other', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-sm font-arial h-10 box-border"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block mb-2 font-semibold text-sm text-gray-600">
|
||||
Required Library Size <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<select
|
||||
value={formData.Required_Library_Size}
|
||||
onChange={(e) => handleChange('Required_Library_Size', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-sm font-arial h-10 box-border"
|
||||
required
|
||||
>
|
||||
{librarySizeOptions.map(option => (
|
||||
<option key={option} value={option} style={{ color: '#555555' }}>
|
||||
{option || 'Select'}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block mb-2 font-semibold text-sm text-gray-600">Please Specify (if Others)</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.Required_Library_Size_Other}
|
||||
onChange={(e) => handleChange('Required_Library_Size_Other', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-sm font-arial h-10 box-border"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block mb-2 font-semibold text-sm text-gray-600">Index Information (only for RTRL)</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.Index_Information}
|
||||
onChange={(e) => handleChange('Index_Information', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-sm font-arial h-10 box-border"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block mb-2 font-semibold text-sm text-gray-600">Kit Information (only for RTRL)</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.Kit_Information}
|
||||
onChange={(e) => handleChange('Kit_Information', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-sm font-arial h-10 box-border"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block mb-2 font-semibold text-sm text-gray-600">
|
||||
Sequencing Platform <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<select
|
||||
value={formData.Sequencing_Platform}
|
||||
onChange={(e) => handleChange('Sequencing_Platform', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-sm font-arial h-10 box-border"
|
||||
required
|
||||
>
|
||||
{platformOptions.map(option => (
|
||||
<option key={option} value={option} style={{ color: '#555555' }}>
|
||||
{option || 'Select'}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block mb-2 font-semibold text-sm text-gray-600">Please Specify (if Others)</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.Sequencing_Platform_Other}
|
||||
onChange={(e) => handleChange('Sequencing_Platform_Other', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-sm font-arial h-10 box-border"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block mb-2 font-semibold text-sm text-gray-600">
|
||||
Sequencing Read Length <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<select
|
||||
value={formData.Sequencing_Read_Length}
|
||||
onChange={(e) => handleChange('Sequencing_Read_Length', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-sm font-arial h-10 box-border"
|
||||
required
|
||||
>
|
||||
{readLengthOptions.map(option => (
|
||||
<option key={option} value={option} style={{ color: '#555555' }}>
|
||||
{option || 'Select'}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block mb-2 font-semibold text-sm text-gray-600">Please Specify (if Others)</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.Sequencing_Read_Length_Other}
|
||||
onChange={(e) => handleChange('Sequencing_Read_Length_Other', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-sm font-arial h-10 box-border"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block mb-2 font-semibold text-sm text-gray-600">
|
||||
Total Data Requirement (in Million or in GB) <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.Total_Data_Requirement}
|
||||
onChange={(e) => handleChange('Total_Data_Requirement', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-sm font-arial h-10 box-border"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block mb-2 font-semibold text-sm text-gray-600">Remarks</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.Service_Remarks}
|
||||
onChange={(e) => handleChange('Service_Remarks', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-2 border border-gray-300 rounded text-sm font-arial h-10 box-border"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ServiceInfoSection;
|
||||
41
app/components/SampleGuideline/ContentSection.jsx
Normal file
41
app/components/SampleGuideline/ContentSection.jsx
Normal file
@ -0,0 +1,41 @@
|
||||
// ContentSection.jsx - Simple with Left Sidebar
|
||||
'use client';
|
||||
import React, { useState } from 'react';
|
||||
import Sidebar from './Sidebar';
|
||||
import GeneralGuidelines from './GeneralGuidelines';
|
||||
import SearchSampleRequirements from './SearchSampleRequirements';
|
||||
|
||||
const ContentSection = () => {
|
||||
const [activeSection, setActiveSection] = useState('general-content');
|
||||
|
||||
const renderContent = () => {
|
||||
const sections = {
|
||||
'general-content': <GeneralGuidelines />,
|
||||
'search-content': <SearchSampleRequirements />
|
||||
};
|
||||
return sections[activeSection] || <GeneralGuidelines />;
|
||||
};
|
||||
|
||||
return (
|
||||
<section className="pt-4 pb-8">
|
||||
<div className="container mx-auto max-w-none px-4">
|
||||
<div className="grid xl:grid-cols-[280px_1fr] gap-8">
|
||||
{/* LEFT SIDEBAR */}
|
||||
<div className="xl:sticky xl:top-8">
|
||||
<Sidebar
|
||||
activeSection={activeSection}
|
||||
onSectionChange={setActiveSection}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* RIGHT CONTENT */}
|
||||
<div className="bg-white rounded-lg">
|
||||
{renderContent()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default ContentSection;
|
||||
54
app/components/SampleGuideline/GeneralGuidelines.jsx
Normal file
54
app/components/SampleGuideline/GeneralGuidelines.jsx
Normal file
@ -0,0 +1,54 @@
|
||||
import React from 'react';
|
||||
|
||||
const GeneralGuidelines = () => {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="mb-6">
|
||||
<h3 className="text-2xl font-semibold text-gray-600 mb-4">General Guidelines</h3>
|
||||
</div>
|
||||
|
||||
<div className="prose max-w-none">
|
||||
<ul className="space-y-4 text-gray-700 leading-relaxed pl-5">
|
||||
<li className="list-disc">
|
||||
Please complete the Sample Initiation Form (SIF), ensuring that the
|
||||
sample names on the form match the labels on the sample tubes. We also
|
||||
request that you send an electronic copy of the form and any required QC
|
||||
data via email.
|
||||
</li>
|
||||
<li className="list-disc">
|
||||
Each tube should be labeled on the lid with a maximum of 4-6
|
||||
alphanumeric characters (e.g., 4B0001). Use a black permanent marker to
|
||||
write sample names on the top and side of each tube. Avoid writing
|
||||
directly on the tube wall or cover with an oil pen.
|
||||
</li>
|
||||
<li className="list-disc">
|
||||
DNA can be submitted in DNase-free water, Elution Buffer, or 10mM Tris
|
||||
pH 8.0. DNA samples should have an OD260/280 ratio as close to 1.8~2.0
|
||||
as possible. All DNA should be RNase-treated and free from degradation
|
||||
or contamination. Ship with ice packs. The total amount of DNA required
|
||||
depends on the specific application.
|
||||
</li>
|
||||
<li className="list-disc">
|
||||
RNA can be submitted in RNase-free water, RNA Stabilization Reagent, or
|
||||
10mM Tris pH 8.0. All total RNA samples should be DNA-free, with an OD
|
||||
A260/A280 ratio ≥ 1.8, A260/230 ratio ≥ 1.8, and a RIN ≥ 6. Ship with
|
||||
dry ice. The total amount of RNA required depends on the specific
|
||||
application. For Long Read Sequencing, RNA samples should have a RIN ≥
|
||||
8.
|
||||
</li>
|
||||
<li className="list-disc">
|
||||
The listed concentrations should be determined by fluorometry (e.g.,
|
||||
PicoGreen/Qubit/RiboGreen). If using spectrophotometry (e.g., Nanodrop),
|
||||
increase concentrations by approximately twofold.
|
||||
</li>
|
||||
<li className="list-disc">
|
||||
The quality inspection method for the sizes and concentrations of the
|
||||
Ready To Run Library is Qubit and Agilent Bioanalyzer.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default GeneralGuidelines;
|
||||
23
app/components/SampleGuideline/IntroSection.jsx
Normal file
23
app/components/SampleGuideline/IntroSection.jsx
Normal file
@ -0,0 +1,23 @@
|
||||
import React from 'react';
|
||||
|
||||
const IntroSection = () => {
|
||||
return (
|
||||
<section className="bg-white mx-4 md:mx-8 mt-4 mb-4 py-4">
|
||||
<div className="container mx-auto max-w-none px-4">
|
||||
<div className="text-gray-600 max-w-none leading-relaxed text-center">
|
||||
<div className="text-base text-justify">
|
||||
<p className="m-0">
|
||||
We humbly offer a wide range of services, including genomics, transcriptomics,
|
||||
metagenomics, epigenomics, single-cell sequencing, genotyping, microarray,
|
||||
bioinformatics, and more. To help us deliver the best results for you, we request you to
|
||||
review our sample requirements and follow the instructions for packaging, labeling, and
|
||||
shipping your samples.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default IntroSection;
|
||||
41
app/components/SampleGuideline/PageTitle.jsx
Normal file
41
app/components/SampleGuideline/PageTitle.jsx
Normal file
@ -0,0 +1,41 @@
|
||||
import React from 'react';
|
||||
|
||||
const PageTitle = () => {
|
||||
return (
|
||||
<section
|
||||
className="relative bg-cover bg-center py-4 sm:py-6 h-auto sm:h-32 md:h-40 lg:h-24 min-h-[120px] sm:min-h-[140px]"
|
||||
style={{ backgroundImage: "url('images/bredcrumb.jpg')" }}
|
||||
>
|
||||
{/* Breadcrumb */}
|
||||
<div className="relative z-10 mb-2 sm:mb-1 pt-2 sm:pt-0 sm:-mt-3 lg:-mt-3">
|
||||
<div className="container mx-auto max-w-none px-4">
|
||||
<nav className="flex flex-wrap items-center gap-1 sm:gap-2 text-xs sm:text-sm lg:text-sm">
|
||||
<a href="/" className="text-white hover:text-yellow-400 underline whitespace-nowrap">Home</a>
|
||||
<span className="text-white flex-shrink-0">
|
||||
<svg className="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clipRule="evenodd" />
|
||||
</svg>
|
||||
</span>
|
||||
<a href="/sample-submission-guideline" className="text-white hover:text-yellow-400 underline whitespace-nowrap">Knowledge Hub</a>
|
||||
<span className="text-white flex-shrink-0">
|
||||
<svg className="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clipRule="evenodd" />
|
||||
</svg>
|
||||
</span>
|
||||
<span className="text-white">Sample Submission Guideline</span>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Page Title */}
|
||||
<div className="relative z-10 text-center pb-2 sm:pb-0 sm:-mt-2 lg:mt-4">
|
||||
<h1 className="text-lg sm:text-2xl md:text-3xl lg:text-4xl xl:text-4xl font-bold text-white mb-2 px-4 leading-tight">
|
||||
Sample Submission Guideline
|
||||
</h1>
|
||||
<div className="w-12 sm:w-14 md:w-16 lg:w-16 h-1 bg-yellow-400 mx-auto"></div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default PageTitle;
|
||||
15
app/components/SampleGuideline/SampleGuidelinePage.jsx
Normal file
15
app/components/SampleGuideline/SampleGuidelinePage.jsx
Normal file
@ -0,0 +1,15 @@
|
||||
import React from 'react';
|
||||
import PageTitle from './PageTitle';
|
||||
import IntroSection from './IntroSection';
|
||||
import ContentSection from './ContentSection';
|
||||
|
||||
const SampleGuidelinePage = () => {
|
||||
return (
|
||||
<div className="page-content">
|
||||
<PageTitle />
|
||||
<IntroSection />
|
||||
<ContentSection />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export default SampleGuidelinePage;
|
||||
154
app/components/SampleGuideline/SearchFilters.jsx
Normal file
154
app/components/SampleGuideline/SearchFilters.jsx
Normal file
@ -0,0 +1,154 @@
|
||||
'use client';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { sequencingData } from './sequencingData';
|
||||
import TooltipIcon from './TooltipIcon';
|
||||
|
||||
const SearchFilters = ({ filters, onFilterChange, onSearch, onReset, isSearchEnabled }) => {
|
||||
const [availableOptions, setAvailableOptions] = useState({
|
||||
nucleicAcid: [],
|
||||
category: [],
|
||||
application: [],
|
||||
sampleType: []
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
updateAvailableOptions();
|
||||
}, [filters]);
|
||||
|
||||
const updateAvailableOptions = () => {
|
||||
// Get nucleic acid options
|
||||
const nucleicAcids = [...new Set(sequencingData.map(item => item.nucleicAcid))];
|
||||
|
||||
// Filter data based on current selections
|
||||
let filteredData = sequencingData;
|
||||
if (filters.nucleicAcid) {
|
||||
filteredData = filteredData.filter(item => item.nucleicAcid === filters.nucleicAcid);
|
||||
}
|
||||
|
||||
const categories = [...new Set(filteredData.map(item => item.category))];
|
||||
|
||||
// Further filter for applications
|
||||
if (filters.category) {
|
||||
filteredData = filteredData.filter(item => item.category === filters.category);
|
||||
}
|
||||
|
||||
const applications = [...new Set(filteredData.map(item => item.application))];
|
||||
|
||||
// Further filter for sample types
|
||||
if (filters.application) {
|
||||
filteredData = filteredData.filter(item => item.application === filters.application);
|
||||
}
|
||||
|
||||
const sampleTypes = [...new Set(filteredData.map(item => item.sampleType))];
|
||||
|
||||
setAvailableOptions({
|
||||
nucleicAcid: nucleicAcids.sort(),
|
||||
category: categories.sort(),
|
||||
application: applications.sort(),
|
||||
sampleType: sampleTypes.sort()
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-white p-6 rounded-lg shadow-sm border">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
{/* Nucleic Acid */}
|
||||
<div className="space-y-2">
|
||||
<label className="flex items-center text-sm font-semibold text-gray-700">
|
||||
Nucleic Acid
|
||||
</label>
|
||||
<select
|
||||
value={filters.nucleicAcid}
|
||||
onChange={(e) => onFilterChange('nucleicAcid', e.target.value)}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-3 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||
>
|
||||
<option value="" style={{ color: '#555555' }}>Select</option>
|
||||
{availableOptions.nucleicAcid.map(option => (
|
||||
<option key={option} value={option} style={{ color: '#555555' }}>{option}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* Category */}
|
||||
<div className="space-y-2">
|
||||
<label className="flex items-center text-sm font-semibold text-gray-700">
|
||||
Category
|
||||
</label>
|
||||
<select
|
||||
value={filters.category}
|
||||
onChange={(e) => onFilterChange('category', e.target.value)}
|
||||
disabled={!filters.nucleicAcid}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-3 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent disabled:bg-gray-100 disabled:cursor-not-allowed"
|
||||
>
|
||||
<option value="" style={{ color: '#555555' }}>{filters.nucleicAcid ? 'Select' : 'Select Previous Option First'}</option>
|
||||
{availableOptions.category.map(option => (
|
||||
<option key={option} value={option} style={{ color: '#555555' }}>{option}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* Applications */}
|
||||
<div className="space-y-2">
|
||||
<label className="flex items-center text-sm font-semibold text-gray-700">
|
||||
Applications
|
||||
<TooltipIcon text="Lists all the applications for the sample." />
|
||||
</label>
|
||||
<select
|
||||
value={filters.application}
|
||||
onChange={(e) => onFilterChange('application', e.target.value)}
|
||||
disabled={!filters.category}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-3 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent disabled:bg-gray-100 disabled:cursor-not-allowed"
|
||||
>
|
||||
<option value="" style={{ color: '#555555' }}>{filters.category ? 'Select' : 'Select Previous Option First'}</option>
|
||||
{availableOptions.application.map(option => (
|
||||
<option key={option} value={option} style={{ color: '#555555' }}>{option}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* Sample Type */}
|
||||
<div className="space-y-2">
|
||||
<label className="flex items-center text-sm font-semibold text-gray-700">
|
||||
Sample Type
|
||||
<TooltipIcon text="Describes the nature of the sample. Common sample types are listed, though some categories might not be included." />
|
||||
</label>
|
||||
<select
|
||||
value={filters.sampleType}
|
||||
onChange={(e) => onFilterChange('sampleType', e.target.value)}
|
||||
disabled={!filters.application}
|
||||
style={{ color: '#555555' }}
|
||||
className="w-full p-3 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent disabled:bg-gray-100 disabled:cursor-not-allowed"
|
||||
>
|
||||
<option value="" style={{ color: '#555555' }}>{filters.application ? 'Select' : 'Select Previous Option First'}</option>
|
||||
{availableOptions.sampleType.map(option => (
|
||||
<option key={option} value={option} style={{ color: '#555555' }}>{option}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Buttons */}
|
||||
<div className="flex justify-end items-center space-x-4 mt-6">
|
||||
<button
|
||||
onClick={onReset}
|
||||
className="px-4 py-2 text-gray-600 hover:text-gray-800 transition-colors"
|
||||
title="Reset filters"
|
||||
>
|
||||
✖
|
||||
</button>
|
||||
<button
|
||||
onClick={onSearch}
|
||||
disabled={!isSearchEnabled}
|
||||
className="px-6 py-2 bg-teal-600 text-white font-semibold rounded-md hover:bg-teal-700 disabled:bg-gray-300 disabled:cursor-not-allowed transition-colors"
|
||||
>
|
||||
Search
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SearchFilters;
|
||||
72
app/components/SampleGuideline/SearchResults.jsx
Normal file
72
app/components/SampleGuideline/SearchResults.jsx
Normal file
@ -0,0 +1,72 @@
|
||||
import React from 'react';
|
||||
import TooltipIcon from './TooltipIcon';
|
||||
|
||||
const SearchResults = ({ results }) => {
|
||||
if (results.length === 0) {
|
||||
return (
|
||||
<div className="mt-6">
|
||||
<h2 className="text-2xl font-semibold text-teal-600 mb-4">Search Result</h2>
|
||||
<div className="text-center py-8 text-gray-600 bg-gray-50 rounded-lg">
|
||||
Please Select Valid Information
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mt-6 space-y-4">
|
||||
<h2 className="text-2xl font-semibold text-teal-600 mb-4">Search Result</h2>
|
||||
|
||||
{results.map((item, index) => (
|
||||
<div key={index} className="bg-teal-50 p-6 rounded-lg shadow-sm">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
<ResultItem
|
||||
title="Recommended Quantity"
|
||||
value={item.recommendedQuantity}
|
||||
tooltip="Indicates the suggested amount of sample for optimal results."
|
||||
/>
|
||||
<ResultItem
|
||||
title="Minimum Quantity"
|
||||
value={item.minimumQuantity}
|
||||
tooltip="Specifies the minimum amount of sample that can be processed."
|
||||
/>
|
||||
<ResultItem
|
||||
title="Minimum Concentration"
|
||||
value={item.minimumConcentration}
|
||||
tooltip="States the lowest concentration of nucleic acid required for processing."
|
||||
/>
|
||||
<ResultItem
|
||||
title="Platform"
|
||||
value={item.platform}
|
||||
tooltip="Identifies the sequencing or analysis platform. This category is applicable only for Ready To Run Library (RTRL)."
|
||||
/>
|
||||
<ResultItem
|
||||
title="Data Amount"
|
||||
value={item.dataAmount}
|
||||
tooltip="Provides an estimate of the data output. This category is applicable only for Ready To Run Library (RTRL)."
|
||||
/>
|
||||
<ResultItem
|
||||
title="Volume Requirement"
|
||||
value={item.volumeRequirement}
|
||||
tooltip="Specifies the necessary volume of the sample for submission. This category is applicable only for Ready To Run Library (RTRL)."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const ResultItem = ({ title, value, tooltip }) => (
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center text-gray-600 text-sm">
|
||||
{title}
|
||||
<TooltipIcon text={tooltip} />
|
||||
</div>
|
||||
<div className="font-semibold text-gray-900 text-base">
|
||||
{value || 'N/A'}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default SearchResults;
|
||||
88
app/components/SampleGuideline/SearchSampleRequirements.jsx
Normal file
88
app/components/SampleGuideline/SearchSampleRequirements.jsx
Normal file
@ -0,0 +1,88 @@
|
||||
'use client';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { sequencingData } from './sequencingData';
|
||||
import SearchFilters from './SearchFilters';
|
||||
import SearchResults from './SearchResults';
|
||||
|
||||
const SearchSampleRequirements = () => {
|
||||
const [filters, setFilters] = useState({
|
||||
nucleicAcid: '',
|
||||
category: '',
|
||||
application: '',
|
||||
sampleType: ''
|
||||
});
|
||||
|
||||
const [filteredResults, setFilteredResults] = useState([]);
|
||||
const [showResults, setShowResults] = useState(false);
|
||||
|
||||
const handleFilterChange = (filterName, value) => {
|
||||
setFilters(prev => {
|
||||
const newFilters = { ...prev, [filterName]: value };
|
||||
|
||||
// Reset subsequent filters when a parent filter changes
|
||||
const filterOrder = ['nucleicAcid', 'category', 'application', 'sampleType'];
|
||||
const currentIndex = filterOrder.indexOf(filterName);
|
||||
|
||||
for (let i = currentIndex + 1; i < filterOrder.length; i++) {
|
||||
newFilters[filterOrder[i]] = '';
|
||||
}
|
||||
|
||||
return newFilters;
|
||||
});
|
||||
};
|
||||
|
||||
const handleSearch = () => {
|
||||
let results = sequencingData;
|
||||
|
||||
if (filters.nucleicAcid) {
|
||||
results = results.filter(item => item.nucleicAcid === filters.nucleicAcid);
|
||||
}
|
||||
if (filters.category) {
|
||||
results = results.filter(item => item.category === filters.category);
|
||||
}
|
||||
if (filters.application) {
|
||||
results = results.filter(item => item.application === filters.application);
|
||||
}
|
||||
if (filters.sampleType) {
|
||||
results = results.filter(item => item.sampleType === filters.sampleType);
|
||||
}
|
||||
|
||||
setFilteredResults(results);
|
||||
setShowResults(true);
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
setFilters({
|
||||
nucleicAcid: '',
|
||||
category: '',
|
||||
application: '',
|
||||
sampleType: ''
|
||||
});
|
||||
setFilteredResults([]);
|
||||
setShowResults(false);
|
||||
};
|
||||
|
||||
const isSearchEnabled = filters.nucleicAcid && filters.category && filters.application && filters.sampleType;
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="mb-6">
|
||||
<h3 className="text-2xl font-semibold text-gray-600">Search Sample Requirements</h3>
|
||||
</div>
|
||||
|
||||
<SearchFilters
|
||||
filters={filters}
|
||||
onFilterChange={handleFilterChange}
|
||||
onSearch={handleSearch}
|
||||
onReset={handleReset}
|
||||
isSearchEnabled={isSearchEnabled}
|
||||
/>
|
||||
|
||||
{showResults && (
|
||||
<SearchResults results={filteredResults} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SearchSampleRequirements;
|
||||
43
app/components/SampleGuideline/Sidebar.jsx
Normal file
43
app/components/SampleGuideline/Sidebar.jsx
Normal file
@ -0,0 +1,43 @@
|
||||
import React from 'react';
|
||||
|
||||
const Sidebar = ({ activeSection, onSectionChange }) => {
|
||||
const menuItems = [
|
||||
{ id: 'general-content', label: '1. General Guidelines' },
|
||||
{ id: 'search-content', label: '2. Search Sample Requirements' }
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
{menuItems.map((item) => (
|
||||
<button
|
||||
key={item.id}
|
||||
onClick={() => onSectionChange(item.id)}
|
||||
className={`w-full text-left px-6 py-4 text-base font-medium rounded-full transition-all duration-300 border-2 whitespace-nowrap ${
|
||||
activeSection === item.id
|
||||
? 'text-white shadow-lg border-transparent'
|
||||
: 'bg-white text-gray-600 border-transparent hover:border-2'
|
||||
}`}
|
||||
style={
|
||||
activeSection === item.id
|
||||
? { backgroundColor: '#ffa72a' }
|
||||
: {}
|
||||
}
|
||||
onMouseEnter={(e) => {
|
||||
if (activeSection !== item.id) {
|
||||
e.target.style.borderColor = '#ffa72a';
|
||||
}
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
if (activeSection !== item.id) {
|
||||
e.target.style.borderColor = 'transparent';
|
||||
}
|
||||
}}
|
||||
>
|
||||
{item.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Sidebar;
|
||||
27
app/components/SampleGuideline/TooltipIcon.jsx
Normal file
27
app/components/SampleGuideline/TooltipIcon.jsx
Normal file
@ -0,0 +1,27 @@
|
||||
'use client';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
const TooltipIcon = ({ text }) => {
|
||||
const [showTooltip, setShowTooltip] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="relative inline-block ml-2">
|
||||
<span
|
||||
className="inline-flex items-center justify-center w-4 h-4 text-xs font-bold text-teal-600 border-2 border-teal-600 rounded-full cursor-pointer hover:bg-teal-600 hover:text-white transition-all duration-200"
|
||||
onMouseEnter={() => setShowTooltip(true)}
|
||||
onMouseLeave={() => setShowTooltip(false)}
|
||||
>
|
||||
i
|
||||
</span>
|
||||
|
||||
{showTooltip && (
|
||||
<div className="absolute bottom-full left-1/2 transform -translate-x-1/2 mb-2 px-3 py-2 text-xs text-white bg-gray-800 rounded-md shadow-lg z-50 w-56 text-left">
|
||||
{text}
|
||||
<div className="absolute top-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-l-4 border-r-4 border-t-4 border-transparent border-t-gray-800"></div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TooltipIcon;
|
||||
1382
app/components/SampleGuideline/sequencingData.js
Normal file
1382
app/components/SampleGuideline/sequencingData.js
Normal file
File diff suppressed because it is too large
Load Diff
23
app/components/SampleInitiation/ContactNote.jsx
Normal file
23
app/components/SampleInitiation/ContactNote.jsx
Normal file
@ -0,0 +1,23 @@
|
||||
import React from 'react';
|
||||
import Link from 'next/link';
|
||||
|
||||
const ContactNote = () => {
|
||||
return (
|
||||
<div className="bg-white py-4">
|
||||
<div className="container">
|
||||
<p className="text-gray-700 leading-relaxed ml-6 md:ml-8 lg:ml-8">
|
||||
Please reach out to us by emailing{' '}
|
||||
<Link
|
||||
href="mailto:info@operifytech.com"
|
||||
className="text-blue-600 hover:text-blue-700 transition-colors"
|
||||
>
|
||||
info@operifytech.com
|
||||
</Link>
|
||||
.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ContactNote;
|
||||
41
app/components/SampleInitiation/PageTitle.jsx
Normal file
41
app/components/SampleInitiation/PageTitle.jsx
Normal file
@ -0,0 +1,41 @@
|
||||
import React from 'react';
|
||||
|
||||
const PageTitle = () => {
|
||||
return (
|
||||
<section
|
||||
className="relative bg-cover bg-center py-4 sm:py-6 h-auto sm:h-32 md:h-40 lg:h-24 min-h-[120px] sm:min-h-[140px]"
|
||||
style={{ backgroundImage: "url('images/bredcrumb.jpg')" }}
|
||||
>
|
||||
{/* Breadcrumb */}
|
||||
<div className="relative z-10 mb-2 sm:mb-1 pt-2 sm:pt-0 sm:-mt-3 lg:-mt-3">
|
||||
<div className="container mx-auto max-w-none px-4">
|
||||
<nav className="flex flex-wrap items-center gap-1 sm:gap-2 text-xs sm:text-sm lg:text-sm">
|
||||
<a href="/" className="text-white hover:text-yellow-400 underline whitespace-nowrap">Home</a>
|
||||
<span className="text-white flex-shrink-0">
|
||||
<svg className="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clipRule="evenodd" />
|
||||
</svg>
|
||||
</span>
|
||||
<a href="/sample-submission-guideline" className="text-white hover:text-yellow-400 underline whitespace-nowrap">Knowledge Hub</a>
|
||||
<span className="text-white flex-shrink-0">
|
||||
<svg className="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clipRule="evenodd" />
|
||||
</svg>
|
||||
</span>
|
||||
<span className="text-white">Sample Initiation Form</span>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Page Title */}
|
||||
<div className="relative z-10 text-center pb-2 sm:pb-0 sm:-mt-2 lg:mt-4">
|
||||
<h1 className="text-lg sm:text-2xl md:text-3xl lg:text-3xl xl:text-4xl font-bold text-white mb-2 px-4 leading-tight">
|
||||
Sample Initiation Form
|
||||
</h1>
|
||||
<div className="w-12 sm:w-14 md:w-16 lg:w-16 h-1 bg-yellow-400 mx-auto"></div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default PageTitle;
|
||||
35
app/components/SampleInitiation/ProcessSection.jsx
Normal file
35
app/components/SampleInitiation/ProcessSection.jsx
Normal file
@ -0,0 +1,35 @@
|
||||
import React from 'react';
|
||||
import ProcessSteps from './ProcessSteps';
|
||||
import SubmissionOptions from './SubmissionOptions';
|
||||
|
||||
const ProcessSection = () => {
|
||||
return (
|
||||
<section className="bg-white">
|
||||
<div className="container mx-auto max-w-none px-4">
|
||||
<div className="bg-white p-4 md:p-6">
|
||||
{/* Main Title */}
|
||||
<div className="text-left mb-4">
|
||||
<h2 className="text-2xl md:text-4xl text-gray-600 font-normal">
|
||||
Welcome to Our Online Submission Portal!
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
{/* Two Column Layout */}
|
||||
<div className="flex flex-col lg:flex-row gap-8 lg:gap-20 items-start">
|
||||
{/* Left Column - Process Steps */}
|
||||
<div className="flex-1 min-w-0">
|
||||
<ProcessSteps />
|
||||
</div>
|
||||
|
||||
{/* Right Column - Submission Options */}
|
||||
<div className="flex-1 min-w-0">
|
||||
<SubmissionOptions />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProcessSection;
|
||||
61
app/components/SampleInitiation/ProcessSteps.jsx
Normal file
61
app/components/SampleInitiation/ProcessSteps.jsx
Normal file
@ -0,0 +1,61 @@
|
||||
import React from 'react';
|
||||
|
||||
const ProcessSteps = () => {
|
||||
const steps = [
|
||||
{
|
||||
number: 1,
|
||||
title: "Review Sample Requirements:",
|
||||
items: [
|
||||
<>Before submitting forms and samples, check the <a href="/sample-submission-guideline" className="text-gray-500 underline">sample submission guidelines</a> page and plan your project accordingly.</>,
|
||||
<>Read the <a href="/packaging-and-shipping-guideline" className="text-gray-500 underline">packaging and shipping</a> guidelines and fill out the appropriate sample Initiation form.</>,
|
||||
"Please note that samples will be processed in the order of date received physically in the lab along with duly filled Sample Submission Form. Submitting an online form without sample shipment will not reserve a space in the queue."
|
||||
]
|
||||
},
|
||||
{
|
||||
number: 2,
|
||||
title: "Match Sample Names",
|
||||
items: [
|
||||
"Ensure sample names match what is written on your tubes. Sample names must be between 4-6 characters, using uppercase letters, numbers, and underscores only."
|
||||
]
|
||||
},
|
||||
{
|
||||
number: 3,
|
||||
title: "Ship or Drop-Off Samples:",
|
||||
items: [
|
||||
<>Ship or drop off your samples at the lab with a printed copy of the submission form. We can also pick up the sample from your institution(additional logistic charges will be applicable). Check the <a href="/packaging-and-shipping-guideline#schedule-content" className="text-gray-500 underline">shipping schedule</a> for more details.</>
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{steps.map((step) => (
|
||||
<div key={step.number} className="flex items-start space-x-4">
|
||||
{/* Step Number */}
|
||||
<div
|
||||
className="flex-shrink-0 w-8 h-8 text-white rounded-md flex items-center justify-center text-base font-bold"
|
||||
style={{ backgroundColor: '#faae31' }}
|
||||
>
|
||||
{step.number}
|
||||
</div>
|
||||
|
||||
{/* Step Content */}
|
||||
<div className="flex-1 min-w-0">
|
||||
<h3 className="text-lg md:text-xl font-medium mb-2" style={{ color: '#2a6564' }}>
|
||||
{step.title}
|
||||
</h3>
|
||||
<ul className="list-disc list-inside space-y-2 text-gray-700 leading-relaxed pl-4">
|
||||
{step.items.map((item, index) => (
|
||||
<li key={index} className="text-justify">
|
||||
{item}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProcessSteps;
|
||||
16
app/components/SampleInitiation/SampleInitiationPage.jsx
Normal file
16
app/components/SampleInitiation/SampleInitiationPage.jsx
Normal file
@ -0,0 +1,16 @@
|
||||
import React from 'react';
|
||||
import PageTitle from './PageTitle';
|
||||
import ProcessSection from './ProcessSection';
|
||||
import ContactNote from './ContactNote';
|
||||
|
||||
const SampleInitiationPage = () => {
|
||||
return (
|
||||
<div className="page-content">
|
||||
<PageTitle />
|
||||
<ProcessSection />
|
||||
<ContactNote />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SampleInitiationPage;
|
||||
260
app/components/SampleInitiation/SubmissionOptions.jsx
Normal file
260
app/components/SampleInitiation/SubmissionOptions.jsx
Normal file
@ -0,0 +1,260 @@
|
||||
'use client';
|
||||
import React, { useState } from 'react';
|
||||
import Image from 'next/image';
|
||||
|
||||
const SubmissionOptions = () => {
|
||||
const [isProcessing, setIsProcessing] = useState(false);
|
||||
|
||||
const handleFillManually = () => {
|
||||
window.location.href = '/samples-form';
|
||||
};
|
||||
|
||||
const handleFileUpload = (event) => {
|
||||
const file = event.target.files[0];
|
||||
if (file) {
|
||||
const validTypes = ['.xlsx', '.xls'];
|
||||
const fileExtension = '.' + file.name.split('.').pop().toLowerCase();
|
||||
|
||||
if (validTypes.includes(fileExtension)) {
|
||||
processExcelFile(file);
|
||||
} else {
|
||||
alert('Please select a valid Excel file (.xlsx or .xls)');
|
||||
event.target.value = '';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const processExcelFile = async (file) => {
|
||||
setIsProcessing(true);
|
||||
|
||||
try {
|
||||
// Import XLSX dynamically since it's a client-side library
|
||||
const XLSX = await import('xlsx');
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
try {
|
||||
const data = new Uint8Array(e.target.result);
|
||||
const workbook = XLSX.read(data, { type: 'array' });
|
||||
const sheetName = workbook.SheetNames[0];
|
||||
const worksheet = workbook.Sheets[sheetName];
|
||||
const jsonData = XLSX.utils.sheet_to_json(worksheet, { defval: '' });
|
||||
|
||||
if (jsonData.length > 0) {
|
||||
// Store the Excel data in sessionStorage
|
||||
sessionStorage.setItem('excelData', JSON.stringify(jsonData));
|
||||
sessionStorage.setItem('uploadedFileName', file.name);
|
||||
|
||||
// Redirect to samples_form after a short delay
|
||||
setTimeout(() => {
|
||||
window.location.href = '/samples-form';
|
||||
}, 1000);
|
||||
} else {
|
||||
alert('No valid data found in the Excel file.');
|
||||
setIsProcessing(false);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Excel processing error:', error);
|
||||
alert('Error processing Excel file. Please check the file format and try again.');
|
||||
setIsProcessing(false);
|
||||
}
|
||||
};
|
||||
reader.readAsArrayBuffer(file);
|
||||
} catch (error) {
|
||||
console.error('Error loading XLSX library:', error);
|
||||
alert('Error loading file processor. Please try again.');
|
||||
setIsProcessing(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDownloadTemplate = async () => {
|
||||
try {
|
||||
// Import XLSX dynamically
|
||||
const XLSX = await import('xlsx');
|
||||
|
||||
// Create template data structure with only column headers
|
||||
const templateData = [
|
||||
{
|
||||
// Customer Information
|
||||
'Principal Investigator': '',
|
||||
'Email': '',
|
||||
'Company/Institution': '',
|
||||
'Contact Number': '',
|
||||
'Address': '',
|
||||
'City': '',
|
||||
'State': '',
|
||||
'Pin': '',
|
||||
'Secondary Contact': '',
|
||||
'Secondary Email': '',
|
||||
'Secondary Company/Institution': '',
|
||||
'Secondary Contact Number': '',
|
||||
|
||||
// Sample Information
|
||||
'Project Title': '',
|
||||
'Number of Samples': '',
|
||||
'Sample Type': '',
|
||||
'Sample Type Other': '',
|
||||
'Sample Source': '',
|
||||
'Sample Source Other': '',
|
||||
'Pathogenicity': '',
|
||||
'Sample Remarks': '',
|
||||
|
||||
// Service Information
|
||||
'Service Requested': '',
|
||||
'Service Requested Other': '',
|
||||
'Type of Library': '',
|
||||
'Type of Library Other': '',
|
||||
'Required Library Size': '',
|
||||
'Required Library Size Other': '',
|
||||
'Index Information': '',
|
||||
'Kit Information': '',
|
||||
'Sequencing Platform': '',
|
||||
'Sequencing Platform Other': '',
|
||||
'Sequencing Read Length': '',
|
||||
'Sequencing Read Length Other': '',
|
||||
'Total Data Requirement': '',
|
||||
'Service Remarks': '',
|
||||
|
||||
// Bioinformatics Information
|
||||
'Analysis Requested': '',
|
||||
'Analysis Details': '',
|
||||
'Reference Genome Available': '',
|
||||
'Genome Size': '',
|
||||
'Special Consideration': '',
|
||||
|
||||
// Sample Details
|
||||
'Serial Number': '',
|
||||
'Sample Name': '',
|
||||
'Storage Temp': '',
|
||||
'Preservative Reagent': '',
|
||||
'Temp Information': '',
|
||||
'Comments': ''
|
||||
}
|
||||
];
|
||||
|
||||
// Create workbook and worksheet
|
||||
const workbook = XLSX.utils.book_new();
|
||||
const worksheet = XLSX.utils.json_to_sheet(templateData);
|
||||
|
||||
// Set column widths for better readability
|
||||
const colWidths = Object.keys(templateData[0]).map(() => ({ wch: 20 }));
|
||||
worksheet['!cols'] = colWidths;
|
||||
|
||||
// Add worksheet to workbook
|
||||
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sample Initiation Form');
|
||||
|
||||
// Generate Excel file and download
|
||||
const today = new Date().toISOString().split('T')[0];
|
||||
const filename = `Sample_Initiation_Form_Template_${today}.xlsx`;
|
||||
XLSX.writeFile(workbook, filename);
|
||||
|
||||
// Show success message
|
||||
showMessage('Excel template downloaded successfully!', 'success');
|
||||
} catch (error) {
|
||||
console.error('Error downloading template:', error);
|
||||
alert('Error downloading template. Please try again.');
|
||||
}
|
||||
};
|
||||
|
||||
const showMessage = (message, type) => {
|
||||
// Create and show a toast message
|
||||
const messageDiv = document.createElement('div');
|
||||
messageDiv.className = `fixed top-5 right-5 z-50 max-w-sm p-4 rounded-lg shadow-lg font-medium ${
|
||||
type === 'success'
|
||||
? 'bg-green-100 text-green-800 border border-green-200'
|
||||
: 'bg-red-100 text-red-800 border border-red-200'
|
||||
}`;
|
||||
messageDiv.textContent = message;
|
||||
|
||||
document.body.appendChild(messageDiv);
|
||||
|
||||
// Auto remove after 5 seconds
|
||||
setTimeout(() => {
|
||||
if (messageDiv.parentNode) {
|
||||
messageDiv.remove();
|
||||
}
|
||||
}, 5000);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{/* Title */}
|
||||
<div>
|
||||
<h3 className="text-gray-600 text-lg md:text-xl font-medium mb-6">
|
||||
Based on the convenience, please select one option:
|
||||
</h3>
|
||||
|
||||
{/* Process Image */}
|
||||
<div className="text-center my-6">
|
||||
<Image
|
||||
src="/images/sample-process-steps1.png"
|
||||
alt="Sample Submission Process"
|
||||
width={500}
|
||||
height={300}
|
||||
className="max-w-full h-auto rounded-lg mx-auto"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Action Buttons */}
|
||||
<div className="space-y-2">
|
||||
{/* Main Action Buttons */}
|
||||
<div className="flex flex-col md:flex-row gap-4 items-center justify-center">
|
||||
<button
|
||||
onClick={handleFillManually}
|
||||
className="w-full md:w-auto px-6 py-3 bg-teal-600 text-white font-medium rounded hover:bg-teal-700 transition-colors"
|
||||
>
|
||||
Fill Manually
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => document.getElementById('fileUpload').click()}
|
||||
className="w-full md:w-auto px-6 py-3 bg-transparent border border-gray-300 text-gray-600 font-normal rounded hover:bg-gray-50 transition-colors flex items-center justify-center gap-2"
|
||||
>
|
||||
<Image
|
||||
src="/images/file-icon.svg"
|
||||
alt="File Icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Upload
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Hidden File Input */}
|
||||
<input
|
||||
type="file"
|
||||
id="fileUpload"
|
||||
className="hidden"
|
||||
accept=".xlsx,.xls"
|
||||
onChange={handleFileUpload}
|
||||
/>
|
||||
|
||||
{/* Processing Indicator */}
|
||||
{isProcessing && (
|
||||
<div className="text-center py-4">
|
||||
<div className="inline-block w-8 h-8 border-4 border-gray-200 border-t-teal-600 rounded-full animate-spin mb-2"></div>
|
||||
<p className="text-gray-600">Processing Excel file and redirecting...</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Or Divider */}
|
||||
<div className="text-center text-gray-500 text-base">
|
||||
or
|
||||
</div>
|
||||
|
||||
{/* Download Template Button */}
|
||||
<div className="text-center">
|
||||
<button
|
||||
onClick={handleDownloadTemplate}
|
||||
className="text-teal-600 text-base underline hover:text-teal-700 transition-colors bg-transparent border-none cursor-pointer"
|
||||
>
|
||||
Download form as Excel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SubmissionOptions;
|
||||
64
app/components/Team/TeamCard.jsx
Normal file
64
app/components/Team/TeamCard.jsx
Normal file
@ -0,0 +1,64 @@
|
||||
import React from 'react';
|
||||
|
||||
const TeamCard = ({
|
||||
image,
|
||||
name,
|
||||
position,
|
||||
linkedinUrl,
|
||||
detailUrl
|
||||
}) => {
|
||||
return (
|
||||
<article className="w-full h-full">
|
||||
<div className="rounded-3xl overflow-hidden hover:shadow-lg transition-shadow duration-300 h-full flex flex-col" style={{ backgroundColor: '#f2fcfc' }}>
|
||||
{/* Image Section */}
|
||||
<div className="relative overflow-hidden">
|
||||
<div className="relative group">
|
||||
<img
|
||||
src={image}
|
||||
alt={name}
|
||||
className="w-full h-80 object-cover group-hover:scale-105 transition-transform duration-300"
|
||||
/>
|
||||
{detailUrl && (
|
||||
<a
|
||||
href={detailUrl}
|
||||
className="absolute inset-0 bg-black bg-opacity-0 hover:bg-opacity-20 transition-all duration-300"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content Section - Flexible to fill remaining space */}
|
||||
<div className="p-6 flex-1 flex flex-col justify-between">
|
||||
<div className="mb-4">
|
||||
<h3 className="text-xl font-semibold mb-2 hover:text-teal-600 transition-colors" style={{ color: '#555555' }}>
|
||||
{detailUrl ? (
|
||||
<a href={detailUrl}>{name}</a>
|
||||
) : (
|
||||
name
|
||||
)}
|
||||
</h3>
|
||||
<p className="text-teal-600 text-sm font-medium leading-relaxed">{position}</p>
|
||||
</div>
|
||||
|
||||
{/* LinkedIn Button - Always at bottom */}
|
||||
{linkedinUrl && (
|
||||
<div className="flex justify-start mt-auto">
|
||||
<a
|
||||
href={linkedinUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center justify-center w-10 h-10 bg-blue-600 hover:bg-blue-700 text-white rounded-full transition-colors duration-300"
|
||||
>
|
||||
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M16.338 16.338H13.67V12.16c0-.995-.017-2.277-1.387-2.277-1.39 0-1.601 1.086-1.601 2.207v4.248H8.014v-8.59h2.559v1.174h.037c.356-.675 1.227-1.387 2.526-1.387 2.703 0 3.203 1.778 3.203 4.092v4.711zM5.005 6.575a1.548 1.548 0 11-.003-3.096 1.548 1.548 0 01.003 3.096zm-1.337 9.763H6.34v-8.59H3.667v8.59zM17.668 1H2.328C1.595 1 1 1.581 1 2.298v15.403C1 18.418 1.595 19 2.328 19h15.34c.734 0 1.332-.582 1.332-1.299V2.298C19 1.581 18.402 1 17.668 1z" clipRule="evenodd" />
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
);
|
||||
};
|
||||
|
||||
export default TeamCard;
|
||||
70
app/components/Team/TeamGrid.jsx
Normal file
70
app/components/Team/TeamGrid.jsx
Normal file
@ -0,0 +1,70 @@
|
||||
import React from 'react';
|
||||
import TeamCard from './TeamCard';
|
||||
|
||||
const TeamGrid = () => {
|
||||
const teamMembers = [
|
||||
{
|
||||
id: 1,
|
||||
image: "/images/team/DrAvinash.png",
|
||||
name: "Dr. Avanish Kumar",
|
||||
position: "Promoter",
|
||||
linkedinUrl: "https://www.linkedin.com/in/avanish-kumar-phd-a034791b0/",
|
||||
detailUrl: "/team-member-detail"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
image: "/images/team/Frame 5.png",
|
||||
name: "Dr. Goriparthi Ramakrishna",
|
||||
position: "Lab Head",
|
||||
linkedinUrl: "https://www.linkedin.com/in/goriparthi-ramakrishna/",
|
||||
detailUrl: "/team-member-detail1"
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
image: "/images/team/Frame 6.png",
|
||||
name: "Dr. Divyank Mahajan",
|
||||
position: "Head, Techno-Commercial Strategy",
|
||||
linkedinUrl: "https://in.linkedin.com/in/divyank-mahajan-phd",
|
||||
detailUrl: "/team-member-detail2"
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
image: "/images/team/Frame 4.png",
|
||||
name: "Dr. Mohammed Moquitul Haque",
|
||||
position: "Lead Scientist - Clinical Genomics",
|
||||
linkedinUrl: "https://www.linkedin.com/in/moquitul-haque-phd-9a405561/",
|
||||
detailUrl: "/team-member-detail3"
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
image: "/images/team/Frame 12.png",
|
||||
name: "Richa Malhotra",
|
||||
position: "Business Manager - Clinical Genomic",
|
||||
linkedinUrl: "https://www.linkedin.com/in/richa-malhotra1/",
|
||||
detailUrl: "/team-member-detail4"
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<section className="py-12">
|
||||
<div className="container mx-auto max-w-none px-6">
|
||||
{/* Grid with equal height cards */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-8">
|
||||
{teamMembers.map((member) => (
|
||||
<div key={member.id} className="flex">
|
||||
<TeamCard
|
||||
image={member.image}
|
||||
name={member.name}
|
||||
position={member.position}
|
||||
linkedinUrl={member.linkedinUrl}
|
||||
detailUrl={member.detailUrl}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default TeamGrid;
|
||||
41
app/components/Team/TeamHero.jsx
Normal file
41
app/components/Team/TeamHero.jsx
Normal file
@ -0,0 +1,41 @@
|
||||
import React from 'react';
|
||||
|
||||
const TeamHero = () => {
|
||||
return (
|
||||
<section
|
||||
className="relative bg-cover bg-center py-6 h-24"
|
||||
style={{ backgroundImage: "url('images/bredcrumb.jpg')" }}
|
||||
>
|
||||
{/* Breadcrumb */}
|
||||
<div className="relative z-10 mb-1 -mt-3">
|
||||
<div className="container mx-auto max-w-none px-4">
|
||||
<nav className="flex items-center space-x-2 text-sm">
|
||||
<a href="/" className="text-white hover:text-yellow-400 underline">Home</a>
|
||||
<span className="text-white">
|
||||
<svg className="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clipRule="evenodd" />
|
||||
</svg>
|
||||
</span>
|
||||
<a href="/about-us" className="text-white hover:text-yellow-400 underline">About Us</a>
|
||||
<span className="text-white">
|
||||
<svg className="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clipRule="evenodd" />
|
||||
</svg>
|
||||
</span>
|
||||
<span className="text-white">Our Team</span>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Page Title */}
|
||||
<div className="relative z-10 text-center -mt-2">
|
||||
<h1 className="text-4xl md:text-4xl font-bold text-white mb-2">
|
||||
Our Team
|
||||
</h1>
|
||||
<div className="w-16 h-1 bg-yellow-400 mx-auto"></div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default TeamHero;
|
||||
20
app/components/TeamDetail/TeamMemberContent.jsx
Normal file
20
app/components/TeamDetail/TeamMemberContent.jsx
Normal file
@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
|
||||
const TeamMemberContent = ({ content }) => {
|
||||
return (
|
||||
<div className="">
|
||||
<div className="prose prose-lg max-w-none">
|
||||
{content.map((paragraph, index) => (
|
||||
<div key={index}>
|
||||
<p className="leading-relaxed text-base mb-6" style={{ color: '#555555' }}>
|
||||
{paragraph}
|
||||
</p>
|
||||
{index < content.length - 1 && <div className="h-2"></div>}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TeamMemberContent;
|
||||
30
app/components/TeamDetail/TeamMemberDetail.jsx
Normal file
30
app/components/TeamDetail/TeamMemberDetail.jsx
Normal file
@ -0,0 +1,30 @@
|
||||
import React from 'react';
|
||||
import TeamMemberHero from './TeamMemberHero';
|
||||
import TeamMemberSidebar from './TeamMemberSidebar';
|
||||
import TeamMemberContent from './TeamMemberContent';
|
||||
|
||||
const TeamMemberDetail = ({ memberData }) => {
|
||||
return (
|
||||
<div>
|
||||
<TeamMemberHero memberName={memberData.name} />
|
||||
|
||||
<section className="pt-12 pb-6">
|
||||
<div className="container mx-auto max-w-none px-6">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-4 gap-8">
|
||||
{/* Sidebar - Member Info */}
|
||||
<div className="lg:col-span-1">
|
||||
<TeamMemberSidebar memberData={memberData} />
|
||||
</div>
|
||||
|
||||
{/* Main Content */}
|
||||
<div className="lg:col-span-3">
|
||||
<TeamMemberContent content={memberData.content} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TeamMemberDetail;
|
||||
42
app/components/TeamDetail/TeamMemberHero.jsx
Normal file
42
app/components/TeamDetail/TeamMemberHero.jsx
Normal file
@ -0,0 +1,42 @@
|
||||
import React from 'react';
|
||||
import Link from 'next/link';
|
||||
|
||||
const TeamMemberHero = ({ memberName }) => {
|
||||
return (
|
||||
<section
|
||||
className="relative bg-cover bg-center py-4 sm:py-6 h-auto sm:h-32 md:h-40 lg:h-24 min-h-[120px] sm:min-h-[140px]"
|
||||
style={{ backgroundImage: "url('/images/bredcrumb.jpg')" }}
|
||||
>
|
||||
{/* Breadcrumb */}
|
||||
<div className="relative z-10 mb-2 sm:mb-1 pt-2 sm:pt-0 sm:-mt-3 lg:-mt-3">
|
||||
<div className="container mx-auto max-w-none px-4">
|
||||
<nav className="flex flex-wrap items-center gap-1 sm:gap-2 text-xs sm:text-sm lg:text-sm">
|
||||
<Link href="/" className="text-white hover:text-yellow-400 underline whitespace-nowrap">Home</Link>
|
||||
<span className="text-white flex-shrink-0">
|
||||
<svg className="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clipRule="evenodd" />
|
||||
</svg>
|
||||
</span>
|
||||
<Link href="/our-team" className="text-white hover:text-yellow-400 underline whitespace-nowrap">Team Members</Link>
|
||||
<span className="text-white flex-shrink-0">
|
||||
<svg className="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clipRule="evenodd" />
|
||||
</svg>
|
||||
</span>
|
||||
<span className="text-white">{memberName}</span>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Page Title */}
|
||||
<div className="relative z-10 text-center pb-2 sm:pb-0 sm:-mt-2 lg:mt-4">
|
||||
<h1 className="text-lg sm:text-2xl md:text-3xl lg:text-4xl xl:text-4xl font-bold text-white mb-2 px-4 leading-tight">
|
||||
{memberName}
|
||||
</h1>
|
||||
<div className="w-12 sm:w-14 md:w-16 lg:w-16 h-1 bg-yellow-400 mx-auto"></div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default TeamMemberHero;
|
||||
46
app/components/TeamDetail/TeamMemberSidebar.jsx
Normal file
46
app/components/TeamDetail/TeamMemberSidebar.jsx
Normal file
@ -0,0 +1,46 @@
|
||||
import React from 'react';
|
||||
|
||||
const TeamMemberSidebar = ({ memberData }) => {
|
||||
return (
|
||||
<div className="lg:sticky lg:top-8">
|
||||
<div className="rounded-3xl overflow-hidden" style={{ backgroundColor: '#f2fcfc' }}>
|
||||
{/* Profile Image */}
|
||||
<div className="relative">
|
||||
<img
|
||||
src={memberData.image}
|
||||
alt={memberData.name}
|
||||
className="w-full h-80 object-cover"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Member Info */}
|
||||
<div className="p-6">
|
||||
<div className="text-left">
|
||||
<h2 className="text-3xl font-normal mb-2" style={{ color: '#555555' }}>
|
||||
{memberData.name}
|
||||
</h2>
|
||||
<h4 className="text-base text-teal-600 mb-6 font-normal">
|
||||
{memberData.position}
|
||||
</h4>
|
||||
|
||||
{/* LinkedIn Button */}
|
||||
{memberData.linkedinUrl && (
|
||||
<a
|
||||
href={memberData.linkedinUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center justify-center w-10 h-10 bg-blue-600 hover:bg-blue-700 text-white rounded-full transition-colors duration-300"
|
||||
>
|
||||
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M16.338 16.338H13.67V12.16c0-.995-.017-2.277-1.387-2.277-1.39 0-1.601 1.086-1.601 2.207v4.248H8.014v-8.59h2.559v1.174h.037c.356-.675 1.227-1.387 2.526-1.387 2.703 0 3.203 1.778 3.203 4.092v4.711zM5.005 6.575a1.548 1.548 0 11-.003-3.096 1.548 1.548 0 01.003 3.096zm-1.337 9.763H6.34v-8.59H3.667v8.59zM17.668 1H2.328C1.595 1 1 1.581 1 2.298v15.403C1 18.418 1.595 19 2.328 19h15.34c.734 0 1.332-.582 1.332-1.299V2.298C19 1.581 18.402 1 17.668 1z" clipRule="evenodd" />
|
||||
</svg>
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TeamMemberSidebar;
|
||||
28
app/components/shared/AdvantagesLayout.jsx
Normal file
28
app/components/shared/AdvantagesLayout.jsx
Normal file
@ -0,0 +1,28 @@
|
||||
// components/shared/AdvantagesLayout.jsx
|
||||
|
||||
const AdvantagesLayout = ({
|
||||
title = "Advantages",
|
||||
advantageItems = [],
|
||||
backgroundGradient = "bg-gradient-to-br from-white to-white",
|
||||
titleColor = "text-gray-700"
|
||||
}) => {
|
||||
return (
|
||||
<section className={`py-5 lg:py-8 ${backgroundGradient} rounded-2xl shadow-sm`}>
|
||||
<div className="container-fluid px-4 lg:px-6">
|
||||
<h2 className={`text-2xl lg:text-3xl ${titleColor} text-left pb-2 mb-6 lg:mb-6`}>
|
||||
{title}
|
||||
</h2>
|
||||
|
||||
<div className="text-justify px-4 lg:px-10">
|
||||
<ul className="space-y-4 text-gray-600 text-base leading-relaxed list-disc">
|
||||
{advantageItems.map((item, index) => (
|
||||
<li key={index}>{item}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default AdvantagesLayout;
|
||||
28
app/components/shared/ApplicationsLayout.jsx
Normal file
28
app/components/shared/ApplicationsLayout.jsx
Normal file
@ -0,0 +1,28 @@
|
||||
// components/shared/ApplicationsLayout.jsx
|
||||
|
||||
const ApplicationsLayout = ({
|
||||
title = "Applications",
|
||||
applicationItems = [],
|
||||
backgroundColor = "bg-gray-50",
|
||||
titleColor = "text-gray-700"
|
||||
}) => {
|
||||
return (
|
||||
<section className={`py-5 lg:py-8 ${backgroundColor}`}>
|
||||
<div className="container-fluid px-4 lg:px-6">
|
||||
<h2 className={`text-2xl lg:text-3xl ${titleColor} text-left pb-2 mb-6 lg:mb-6`}>
|
||||
{title}
|
||||
</h2>
|
||||
|
||||
<ul className="list-disc list-inside space-y-4 text-gray-600 leading-relaxed lg:px-10">
|
||||
{applicationItems.map((item, index) => (
|
||||
<li key={index} className="text-base">
|
||||
{item}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default ApplicationsLayout;
|
||||
45
app/components/shared/IntroductionLayout.jsx
Normal file
45
app/components/shared/IntroductionLayout.jsx
Normal file
@ -0,0 +1,45 @@
|
||||
// components/shared/IntroductionLayout.jsx
|
||||
|
||||
const IntroductionLayout = ({
|
||||
title = "Introduction and Workflow",
|
||||
contentItems = [],
|
||||
imageUrl,
|
||||
imageAlt = "",
|
||||
badgeText,
|
||||
badgeSubtext,
|
||||
backgroundColor = "#f8f9fa",
|
||||
badgeColor = "bg-teal-600"
|
||||
}) => {
|
||||
return (
|
||||
<section className="py-0 md:py-12 lg:py-10">
|
||||
<div className="container-fluid px-0">
|
||||
<h2 className="text-2xl lg:text-3xl text-gray-700 text-left pb-2 px-4 lg:px-8 mb-4">
|
||||
{title}
|
||||
</h2>
|
||||
|
||||
{/* Two column layout */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-[1.14fr_1fr] min-h-[140px] lg:min-h-[280px]">
|
||||
{/* Left side content */}
|
||||
<div className="px-6 lg:px-9 py-6 lg:py-0">
|
||||
<ul className="list-disc list-inside space-y-3 text-gray-600 leading-relaxed lg:px-10 text-justify-center">
|
||||
{contentItems.map((item, index) => (
|
||||
<li key={index}>{item}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Right side image */}
|
||||
<div
|
||||
style={{
|
||||
backgroundImage: imageUrl ? `url('${imageUrl}')` : 'none',
|
||||
backgroundColor: backgroundColor
|
||||
}}
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default IntroductionLayout;
|
||||
68
app/components/shared/SpecificationsLayout.jsx
Normal file
68
app/components/shared/SpecificationsLayout.jsx
Normal file
@ -0,0 +1,68 @@
|
||||
// components/shared/SpecificationsLayout.jsx
|
||||
import Link from 'next/link';
|
||||
|
||||
const SpecificationsLayout = ({
|
||||
title = "Service Specifications",
|
||||
titleColor = "text-gray-800",
|
||||
specificationItems = [],
|
||||
backgroundColor = "#e8f5f3",
|
||||
iconBackgroundColor = "bg-teal-600"
|
||||
}) => {
|
||||
return (
|
||||
<section className="py-8 lg:py-12">
|
||||
<div className="container-fluid px-4 lg:px-6">
|
||||
{/* Section Header */}
|
||||
<div className="text-left mb-8">
|
||||
<h2 className={`text-2xl lg:text-3xl ${titleColor} text-left pb-2 mb-6 lg:mb-6`}>
|
||||
{title}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
{/* Specifications Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 max-w-6xl mx-auto">
|
||||
{specificationItems.map((spec, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="relative"
|
||||
>
|
||||
{/* Background Card */}
|
||||
<div
|
||||
className="rounded-3xl p-8 h-full min-h-[280px] flex flex-col"
|
||||
style={{ backgroundColor: backgroundColor }}
|
||||
>
|
||||
{/* Icon Circle */}
|
||||
<div className="flex justify-center mb-6">
|
||||
<div className={`w-16 h-16 ${iconBackgroundColor} rounded-full flex items-center justify-center`}>
|
||||
<img
|
||||
src={spec.icon}
|
||||
className="w-14 h-14 object-contain brightness-0 invert"
|
||||
alt={`${spec.title} Icon`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Title */}
|
||||
<h3 className="text-center text-gray-800 text-lg font-semibold mb-4">
|
||||
{spec.title}
|
||||
</h3>
|
||||
|
||||
{/* Content */}
|
||||
<div className="text-gray-700 text-sm leading-relaxed text-center flex-grow flex items-center justify-center">
|
||||
<div className="w-full">
|
||||
{spec.renderContent ? spec.renderContent() : (
|
||||
<div className="text-gray-600">
|
||||
{spec.content}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default SpecificationsLayout;
|
||||
56
app/components/shared/TitleBar.jsx
Normal file
56
app/components/shared/TitleBar.jsx
Normal file
@ -0,0 +1,56 @@
|
||||
// components/shared/TitleBar.jsx
|
||||
import React from 'react';
|
||||
import Link from 'next/link';
|
||||
|
||||
const TitleBar = ({ title, desc, breadcrumbs, backgroundImage = "/images/bredcrumb.jpg" }) => {
|
||||
return (
|
||||
<section
|
||||
className="relative bg-cover bg-center py-4 sm:py-6 h-auto sm:h-32 md:h-40 lg:h-[12rem] min-h-[120px] sm:min-h-[140px]"
|
||||
style={{ backgroundImage: `url('${backgroundImage}')` }}
|
||||
>
|
||||
{/* Breadcrumb */}
|
||||
<div className="relative z-10 mb-6 sm:mb-5 pt-2 sm:pt-0 sm:-mt-3 lg:-mt-3">
|
||||
<div className="container mx-auto max-w-none px-4">
|
||||
<nav className="flex flex-wrap items-center gap-1 sm:gap-2 text-xs sm:text-sm lg:text-sm">
|
||||
{breadcrumbs.map((crumb, index) => (
|
||||
<React.Fragment key={index}>
|
||||
{crumb.current ? (
|
||||
<span className="text-white whitespace-nowrap">
|
||||
{crumb.label}
|
||||
</span>
|
||||
) : (
|
||||
<Link
|
||||
href={crumb.href}
|
||||
className="text-white hover:text-yellow-400 underline whitespace-nowrap"
|
||||
>
|
||||
{crumb.label}
|
||||
</Link>
|
||||
)}
|
||||
{index < breadcrumbs.length - 1 && (
|
||||
<span className="text-white flex-shrink-0">
|
||||
<svg className="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clipRule="evenodd" />
|
||||
</svg>
|
||||
</span>
|
||||
)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Page Title */}
|
||||
<div className="relative z-10 text-center pb-2 sm:pb-0 sm:-mt-2 lg:mt-2">
|
||||
<h1 className="text-base sm:text-xl md:text-2xl lg:text-3xl xl:text-4xl font-bold text-white mb-2 px-4 leading-tight">
|
||||
{title}
|
||||
</h1>
|
||||
<h3 className="text-base sm:text-xl md:text-2xl lg:text-3xl xl:text-2xl font-bold text-white mb-2 px-4 leading-tight">
|
||||
{desc}
|
||||
</h3>
|
||||
<div className="w-12 sm:w-14 md:w-16 lg:w-16 h-1 bg-yellow-400 mx-auto"></div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default TitleBar;
|
||||
Reference in New Issue
Block a user