Compare commits
No commits in common. "ai-dev" and "master" have entirely different histories.
Binary file not shown.
|
Before Width: | Height: | Size: 960 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 109 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 127 KiB |
@ -6,6 +6,7 @@ const passport = require('passport');
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const bodyParser = require('body-parser');
|
const bodyParser = require('body-parser');
|
||||||
|
const db = require('./db/models');
|
||||||
const config = require('./config');
|
const config = require('./config');
|
||||||
const swaggerUI = require('swagger-ui-express');
|
const swaggerUI = require('swagger-ui-express');
|
||||||
const swaggerJsDoc = require('swagger-jsdoc');
|
const swaggerJsDoc = require('swagger-jsdoc');
|
||||||
@ -15,7 +16,6 @@ const fileRoutes = require('./routes/file');
|
|||||||
const searchRoutes = require('./routes/search');
|
const searchRoutes = require('./routes/search');
|
||||||
const sqlRoutes = require('./routes/sql');
|
const sqlRoutes = require('./routes/sql');
|
||||||
const pexelsRoutes = require('./routes/pexels');
|
const pexelsRoutes = require('./routes/pexels');
|
||||||
const contactFormRoutes = require('./routes/contactForm');
|
|
||||||
|
|
||||||
const openaiRoutes = require('./routes/openai');
|
const openaiRoutes = require('./routes/openai');
|
||||||
|
|
||||||
@ -116,7 +116,6 @@ app.use(bodyParser.json());
|
|||||||
app.use('/api/auth', authRoutes);
|
app.use('/api/auth', authRoutes);
|
||||||
app.use('/api/file', fileRoutes);
|
app.use('/api/file', fileRoutes);
|
||||||
app.use('/api/pexels', pexelsRoutes);
|
app.use('/api/pexels', pexelsRoutes);
|
||||||
app.use('/api/contact-form', contactFormRoutes);
|
|
||||||
app.enable('trust proxy');
|
app.enable('trust proxy');
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,108 +0,0 @@
|
|||||||
const express = require('express');
|
|
||||||
|
|
||||||
const InquiriesService = require('../services/inquiries');
|
|
||||||
const NewsletterSubscribersService = require('../services/newsletter_subscribers');
|
|
||||||
const wrapAsync = require('../helpers').wrapAsync;
|
|
||||||
|
|
||||||
const router = express.Router();
|
|
||||||
|
|
||||||
const inquiryTypes = new Set(['contact_message', 'request_samples', 'get_a_quote', 'partner_with_us']);
|
|
||||||
const buyerTypes = new Set(['roaster', 'importer', 'distributor', 'specialty_buyer', 'wholesaler', 'other']);
|
|
||||||
const incoterms = new Set(['fob', 'cif', 'cfr', 'exw', 'dap', 'other']);
|
|
||||||
const contactMethods = new Set(['email', 'phone', 'whatsapp']);
|
|
||||||
const newsletterSources = new Set(['home_footer', 'home_cta', 'contact_page', 'popup', 'other']);
|
|
||||||
|
|
||||||
const isValidEmail = (value) => /\S+@\S+\.\S+/.test(value || '');
|
|
||||||
|
|
||||||
const badRequest = (message) => {
|
|
||||||
const error = new Error(message);
|
|
||||||
error.code = 400;
|
|
||||||
return error;
|
|
||||||
};
|
|
||||||
|
|
||||||
router.post(
|
|
||||||
'/inquiry',
|
|
||||||
wrapAsync(async (req, res) => {
|
|
||||||
const data = req.body && req.body.data ? req.body.data : {};
|
|
||||||
|
|
||||||
if (!data.full_name || !data.email || !data.company_name || !data.country || !data.message) {
|
|
||||||
throw badRequest('Full name, email, company name, country, and message are required.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isValidEmail(data.email)) {
|
|
||||||
throw badRequest('A valid email address is required.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.inquiry_type && !inquiryTypes.has(data.inquiry_type)) {
|
|
||||||
throw badRequest('Invalid inquiry type.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.buyer_type && !buyerTypes.has(data.buyer_type)) {
|
|
||||||
throw badRequest('Invalid buyer type.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.incoterm && !incoterms.has(data.incoterm)) {
|
|
||||||
throw badRequest('Invalid incoterm.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.preferred_contact_method && !contactMethods.has(data.preferred_contact_method)) {
|
|
||||||
throw badRequest('Invalid preferred contact method.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.target_volume_kg !== undefined && data.target_volume_kg !== null && Number.isNaN(Number(data.target_volume_kg))) {
|
|
||||||
throw badRequest('Target volume must be a number.');
|
|
||||||
}
|
|
||||||
|
|
||||||
await InquiriesService.create({
|
|
||||||
inquiry_type: data.inquiry_type || 'contact_message',
|
|
||||||
status: 'new',
|
|
||||||
full_name: data.full_name,
|
|
||||||
email: data.email,
|
|
||||||
phone: data.phone || null,
|
|
||||||
company_name: data.company_name,
|
|
||||||
country: data.country,
|
|
||||||
buyer_type: data.buyer_type || 'other',
|
|
||||||
message: data.message,
|
|
||||||
target_volume_kg:
|
|
||||||
data.target_volume_kg !== undefined && data.target_volume_kg !== null && data.target_volume_kg !== ''
|
|
||||||
? Number(data.target_volume_kg)
|
|
||||||
: null,
|
|
||||||
incoterm: data.incoterm || 'fob',
|
|
||||||
preferred_contact_method: data.preferred_contact_method || 'email',
|
|
||||||
submitted_at: new Date(),
|
|
||||||
});
|
|
||||||
|
|
||||||
res.status(200).send({ success: true });
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
router.post(
|
|
||||||
'/newsletter',
|
|
||||||
wrapAsync(async (req, res) => {
|
|
||||||
const data = req.body && req.body.data ? req.body.data : {};
|
|
||||||
|
|
||||||
if (!data.email || !isValidEmail(data.email)) {
|
|
||||||
throw badRequest('A valid email address is required.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.source && !newsletterSources.has(data.source)) {
|
|
||||||
throw badRequest('Invalid newsletter source.');
|
|
||||||
}
|
|
||||||
|
|
||||||
await NewsletterSubscribersService.create({
|
|
||||||
email: data.email,
|
|
||||||
full_name: data.full_name || null,
|
|
||||||
company_name: data.company_name || null,
|
|
||||||
country: data.country || null,
|
|
||||||
source: data.source || 'other',
|
|
||||||
is_confirmed: false,
|
|
||||||
subscribed_at: new Date(),
|
|
||||||
});
|
|
||||||
|
|
||||||
res.status(200).send({ success: true });
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
router.use('/', require('../helpers').commonErrorHandler);
|
|
||||||
|
|
||||||
module.exports = router;
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 664 KiB |
@ -1,5 +1,6 @@
|
|||||||
import React, { useEffect, useRef, useState } from 'react'
|
import React, {useEffect, useRef} from 'react'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
|
import { useState } from 'react'
|
||||||
import { mdiChevronUp, mdiChevronDown } from '@mdi/js'
|
import { mdiChevronUp, mdiChevronDown } from '@mdi/js'
|
||||||
import BaseDivider from './BaseDivider'
|
import BaseDivider from './BaseDivider'
|
||||||
import BaseIcon from './BaseIcon'
|
import BaseIcon from './BaseIcon'
|
||||||
|
|||||||
@ -1,230 +0,0 @@
|
|||||||
import axios from 'axios';
|
|
||||||
import React, { useEffect, useMemo, useState } from 'react';
|
|
||||||
|
|
||||||
import MarketingButton from './MarketingButton';
|
|
||||||
import { buyerTypes, contactMethods, incoterms, inquiryTypes } from './marketingData';
|
|
||||||
|
|
||||||
type InquiryType = 'request_samples' | 'get_a_quote' | 'partner_with_us' | 'contact_message';
|
|
||||||
type BuyerType = 'roaster' | 'importer' | 'distributor' | 'specialty_buyer' | 'wholesaler' | 'other';
|
|
||||||
type ContactMethod = 'email' | 'phone' | 'whatsapp';
|
|
||||||
type Incoterm = 'fob' | 'cif' | 'cfr' | 'exw' | 'dap' | 'other';
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
initialInquiryType?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type InquiryState = {
|
|
||||||
inquiry_type: InquiryType;
|
|
||||||
full_name: string;
|
|
||||||
email: string;
|
|
||||||
phone: string;
|
|
||||||
company_name: string;
|
|
||||||
country: string;
|
|
||||||
buyer_type: BuyerType;
|
|
||||||
message: string;
|
|
||||||
target_volume_kg: string;
|
|
||||||
incoterm: Incoterm;
|
|
||||||
preferred_contact_method: ContactMethod;
|
|
||||||
};
|
|
||||||
|
|
||||||
const defaultState: InquiryState = {
|
|
||||||
inquiry_type: 'request_samples',
|
|
||||||
full_name: '',
|
|
||||||
email: '',
|
|
||||||
phone: '',
|
|
||||||
company_name: '',
|
|
||||||
country: '',
|
|
||||||
buyer_type: 'importer',
|
|
||||||
message: '',
|
|
||||||
target_volume_kg: '',
|
|
||||||
incoterm: 'fob',
|
|
||||||
preferred_contact_method: 'email',
|
|
||||||
};
|
|
||||||
|
|
||||||
const allowedInquiryTypes = new Set(['request_samples', 'get_a_quote', 'partner_with_us', 'contact_message']);
|
|
||||||
|
|
||||||
export default function InquiryForm({ initialInquiryType }: Props) {
|
|
||||||
const [form, setForm] = useState<InquiryState>(defaultState);
|
|
||||||
const [errorMessage, setErrorMessage] = useState('');
|
|
||||||
const [successMessage, setSuccessMessage] = useState('');
|
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (initialInquiryType && allowedInquiryTypes.has(initialInquiryType)) {
|
|
||||||
setForm((current) => ({
|
|
||||||
...current,
|
|
||||||
inquiry_type: initialInquiryType as InquiryType,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}, [initialInquiryType]);
|
|
||||||
|
|
||||||
const title = useMemo(() => {
|
|
||||||
const currentType = inquiryTypes.find((item) => item.value === form.inquiry_type);
|
|
||||||
return currentType ? currentType.label : 'Inquiry';
|
|
||||||
}, [form.inquiry_type]);
|
|
||||||
|
|
||||||
const onChange = (
|
|
||||||
event: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>,
|
|
||||||
) => {
|
|
||||||
setForm((current) => ({
|
|
||||||
...current,
|
|
||||||
[event.target.name]: event.target.value,
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
const onSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
|
|
||||||
event.preventDefault();
|
|
||||||
setErrorMessage('');
|
|
||||||
setSuccessMessage('');
|
|
||||||
|
|
||||||
if (!form.full_name.trim() || !form.email.trim() || !form.company_name.trim() || !form.country.trim() || !form.message.trim()) {
|
|
||||||
setErrorMessage('Please complete name, email, company, country, and message.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setIsSubmitting(true);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await axios.post('/contact-form/inquiry', {
|
|
||||||
data: {
|
|
||||||
...form,
|
|
||||||
target_volume_kg: form.target_volume_kg ? Number(form.target_volume_kg) : null,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
setSuccessMessage(`Thank you. Your ${title.toLowerCase()} request has been received and our team will respond shortly.`);
|
|
||||||
setForm((current) => ({
|
|
||||||
...defaultState,
|
|
||||||
inquiry_type: current.inquiry_type,
|
|
||||||
}));
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Inquiry submission failed', error);
|
|
||||||
setErrorMessage('Your request could not be submitted right now. Please try again or email info@alemdesta.com.');
|
|
||||||
} finally {
|
|
||||||
setIsSubmitting(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="rounded-[2rem] border border-[#1F3A2D]/10 bg-white p-6 shadow-[0_24px_80px_rgba(20,38,29,0.08)] md:p-8">
|
|
||||||
<div className="mb-6">
|
|
||||||
<p className="text-sm font-semibold uppercase tracking-[0.35em] text-[#9FB06F]">Buyer inquiry</p>
|
|
||||||
<h3 className="font-brand-display mt-3 text-3xl text-[#1F3A2D]">{title}</h3>
|
|
||||||
<p className="mt-3 max-w-2xl text-sm leading-7 text-[#556257]">
|
|
||||||
Share your sourcing goals and we will tailor a response around origins, order size, process, and export handling.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form className="space-y-4" onSubmit={onSubmit}>
|
|
||||||
<div className="grid gap-4 md:grid-cols-2">
|
|
||||||
<label className="space-y-2 text-sm font-medium text-[#1F3A2D]">
|
|
||||||
Inquiry type
|
|
||||||
<select className="coffee-input" name="inquiry_type" value={form.inquiry_type} onChange={onChange}>
|
|
||||||
{inquiryTypes.map((item) => (
|
|
||||||
<option key={item.value} value={item.value}>
|
|
||||||
{item.label}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</label>
|
|
||||||
<label className="space-y-2 text-sm font-medium text-[#1F3A2D]">
|
|
||||||
Buyer type
|
|
||||||
<select className="coffee-input" name="buyer_type" value={form.buyer_type} onChange={onChange}>
|
|
||||||
{buyerTypes.map((item) => (
|
|
||||||
<option key={item.value} value={item.value}>
|
|
||||||
{item.label}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid gap-4 md:grid-cols-2">
|
|
||||||
<label className="space-y-2 text-sm font-medium text-[#1F3A2D]">
|
|
||||||
Full name
|
|
||||||
<input className="coffee-input" name="full_name" placeholder="Your name" value={form.full_name} onChange={onChange} />
|
|
||||||
</label>
|
|
||||||
<label className="space-y-2 text-sm font-medium text-[#1F3A2D]">
|
|
||||||
Email
|
|
||||||
<input className="coffee-input" name="email" type="email" placeholder="you@company.com" value={form.email} onChange={onChange} />
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid gap-4 md:grid-cols-2">
|
|
||||||
<label className="space-y-2 text-sm font-medium text-[#1F3A2D]">
|
|
||||||
Phone / WhatsApp
|
|
||||||
<input className="coffee-input" name="phone" placeholder="Contact number" value={form.phone} onChange={onChange} />
|
|
||||||
</label>
|
|
||||||
<label className="space-y-2 text-sm font-medium text-[#1F3A2D]">
|
|
||||||
Preferred contact method
|
|
||||||
<select className="coffee-input" name="preferred_contact_method" value={form.preferred_contact_method} onChange={onChange}>
|
|
||||||
{contactMethods.map((item) => (
|
|
||||||
<option key={item.value} value={item.value}>
|
|
||||||
{item.label}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid gap-4 md:grid-cols-2">
|
|
||||||
<label className="space-y-2 text-sm font-medium text-[#1F3A2D]">
|
|
||||||
Company name
|
|
||||||
<input className="coffee-input" name="company_name" placeholder="Company" value={form.company_name} onChange={onChange} />
|
|
||||||
</label>
|
|
||||||
<label className="space-y-2 text-sm font-medium text-[#1F3A2D]">
|
|
||||||
Country
|
|
||||||
<input className="coffee-input" name="country" placeholder="Country" value={form.country} onChange={onChange} />
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid gap-4 md:grid-cols-2">
|
|
||||||
<label className="space-y-2 text-sm font-medium text-[#1F3A2D]">
|
|
||||||
Target volume (kg)
|
|
||||||
<input
|
|
||||||
className="coffee-input"
|
|
||||||
min="0"
|
|
||||||
name="target_volume_kg"
|
|
||||||
placeholder="Optional"
|
|
||||||
type="number"
|
|
||||||
value={form.target_volume_kg}
|
|
||||||
onChange={onChange}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label className="space-y-2 text-sm font-medium text-[#1F3A2D]">
|
|
||||||
Preferred Incoterm
|
|
||||||
<select className="coffee-input" name="incoterm" value={form.incoterm} onChange={onChange}>
|
|
||||||
{incoterms.map((item) => (
|
|
||||||
<option key={item.value} value={item.value}>
|
|
||||||
{item.label}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<label className="space-y-2 text-sm font-medium text-[#1F3A2D]">
|
|
||||||
Message
|
|
||||||
<textarea
|
|
||||||
className="coffee-input min-h-[160px] resize-y pt-3"
|
|
||||||
name="message"
|
|
||||||
placeholder="Tell us the origins, process style, order timing, or partnership goals you are exploring."
|
|
||||||
value={form.message}
|
|
||||||
onChange={onChange}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<div className="flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
|
|
||||||
<p className="text-sm text-[#667367]">Submissions are routed to the admin inquiry queue for follow-up.</p>
|
|
||||||
<MarketingButton className="w-full md:w-auto" type="submit">
|
|
||||||
{isSubmitting ? 'Sending request...' : 'Submit inquiry'}
|
|
||||||
</MarketingButton>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{errorMessage ? <p className="text-sm text-red-600">{errorMessage}</p> : null}
|
|
||||||
{successMessage ? (
|
|
||||||
<div className="rounded-2xl border border-[#9FB06F]/30 bg-[#EEF3E7] px-4 py-3 text-sm text-[#1F3A2D]">{successMessage}</div>
|
|
||||||
) : null}
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,48 +0,0 @@
|
|||||||
import Link from 'next/link';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
href?: string;
|
|
||||||
onClick?: () => void;
|
|
||||||
type?: 'button' | 'submit';
|
|
||||||
variant?: 'primary' | 'secondary' | 'ghost';
|
|
||||||
className?: string;
|
|
||||||
children: React.ReactNode;
|
|
||||||
};
|
|
||||||
|
|
||||||
const baseClassName =
|
|
||||||
'inline-flex items-center justify-center rounded-full px-5 py-3 text-sm font-semibold transition-all duration-300 focus:outline-none focus:ring-2 focus:ring-[#9FB06F] focus:ring-offset-2';
|
|
||||||
|
|
||||||
const variantMap = {
|
|
||||||
primary:
|
|
||||||
'bg-[#9FB06F] text-[#18271F] shadow-[0_16px_40px_rgba(159,176,111,0.25)] hover:-translate-y-0.5 hover:bg-[#B0C482]',
|
|
||||||
secondary:
|
|
||||||
'border border-[#B9C88D]/55 bg-white/12 text-white backdrop-blur hover:border-[#C5D39A] hover:bg-white/24',
|
|
||||||
ghost:
|
|
||||||
'border border-[#1F3A2D]/15 bg-white text-[#1F3A2D] hover:border-[#1F3A2D]/30 hover:bg-[#F4F1E6]',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function MarketingButton({
|
|
||||||
href,
|
|
||||||
onClick,
|
|
||||||
type = 'button',
|
|
||||||
variant = 'primary',
|
|
||||||
className = '',
|
|
||||||
children,
|
|
||||||
}: Props) {
|
|
||||||
const classes = `${baseClassName} ${variantMap[variant]} ${className}`;
|
|
||||||
|
|
||||||
if (href) {
|
|
||||||
return (
|
|
||||||
<Link href={href} className={classes}>
|
|
||||||
{children}
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<button className={classes} onClick={onClick} type={type}>
|
|
||||||
{children}
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,348 +0,0 @@
|
|||||||
import { mdiArrowRight, mdiChevronDown, mdiClose, mdiMenu } from '@mdi/js';
|
|
||||||
import Link from 'next/link';
|
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import React, { useEffect, useMemo, useState } from 'react';
|
|
||||||
|
|
||||||
import BaseIcon from '../BaseIcon';
|
|
||||||
import MarketingButton from './MarketingButton';
|
|
||||||
import NewsletterForm from './NewsletterForm';
|
|
||||||
import { brand, exportServiceItems, navigationItems } from './marketingData';
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
children: React.ReactNode;
|
|
||||||
};
|
|
||||||
|
|
||||||
type NavigationItem = (typeof navigationItems)[number];
|
|
||||||
type NavigationChild = (typeof exportServiceItems)[number];
|
|
||||||
|
|
||||||
const isActivePath = (pathname: string, href: string) => {
|
|
||||||
if (href === '/') {
|
|
||||||
return pathname === '/';
|
|
||||||
}
|
|
||||||
|
|
||||||
return pathname === href;
|
|
||||||
};
|
|
||||||
|
|
||||||
const isNavItemActive = (pathname: string, item: NavigationItem) => {
|
|
||||||
if (isActivePath(pathname, item.href)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return item.children?.some((child) => isActivePath(pathname, child.href)) ?? false;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getCurrentLabel = (pathname: string) => {
|
|
||||||
const currentChild = exportServiceItems.find((item) => item.href === pathname);
|
|
||||||
|
|
||||||
if (currentChild) {
|
|
||||||
return currentChild.label;
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentItem = navigationItems.find((item) => item.href === pathname);
|
|
||||||
return currentItem ? currentItem.label : 'Discover';
|
|
||||||
};
|
|
||||||
|
|
||||||
const getDesktopNavClasses = (isActive: boolean, isHomeHeroMode: boolean) => {
|
|
||||||
if (isHomeHeroMode) {
|
|
||||||
return isActive
|
|
||||||
? 'border border-white/15 bg-white/16 text-white shadow-[0_16px_45px_rgba(0,0,0,0.18)] backdrop-blur-md'
|
|
||||||
: 'border border-white/10 bg-[#173125]/58 text-[#F4F1E6] shadow-[0_14px_35px_rgba(0,0,0,0.18)] backdrop-blur-md hover:bg-[#224032]/74 hover:text-white';
|
|
||||||
}
|
|
||||||
|
|
||||||
return isActive ? 'bg-[#1F3A2D] text-[#F4F1E6]' : 'text-[#32453A] hover:bg-[#F4F1E6]';
|
|
||||||
};
|
|
||||||
|
|
||||||
const getDesktopDropdownClasses = (isHomeHeroMode: boolean) => {
|
|
||||||
return isHomeHeroMode
|
|
||||||
? 'border-white/10 bg-[#14261D]/96 text-white shadow-[0_26px_80px_rgba(0,0,0,0.32)]'
|
|
||||||
: 'border-[#1F3A2D]/10 bg-white text-[#22332B] shadow-[0_26px_80px_rgba(20,38,29,0.14)]';
|
|
||||||
};
|
|
||||||
|
|
||||||
const getDesktopDropdownItemClasses = (pathname: string, item: NavigationChild, isHomeHeroMode: boolean) => {
|
|
||||||
const isActive = isActivePath(pathname, item.href);
|
|
||||||
|
|
||||||
if (isHomeHeroMode) {
|
|
||||||
return isActive
|
|
||||||
? 'bg-white/12 text-white'
|
|
||||||
: 'text-white/78 hover:bg-white/8 hover:text-white';
|
|
||||||
}
|
|
||||||
|
|
||||||
return isActive ? 'bg-[#F4F1E6] text-[#1F3A2D]' : 'text-[#32453A] hover:bg-[#F4F1E6]/70';
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function MarketingLayout({ children }: Props) {
|
|
||||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
|
||||||
const [isExportMenuOpen, setIsExportMenuOpen] = useState(false);
|
|
||||||
const [isScrolled, setIsScrolled] = useState(false);
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const handleScroll = () => {
|
|
||||||
setIsScrolled(window.scrollY > 24);
|
|
||||||
};
|
|
||||||
|
|
||||||
handleScroll();
|
|
||||||
window.addEventListener('scroll', handleScroll, { passive: true });
|
|
||||||
|
|
||||||
return () => window.removeEventListener('scroll', handleScroll);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setIsMenuOpen(false);
|
|
||||||
setIsExportMenuOpen(false);
|
|
||||||
}, [router.pathname]);
|
|
||||||
|
|
||||||
const currentLabel = useMemo(() => getCurrentLabel(router.pathname), [router.pathname]);
|
|
||||||
const isHomeHeroMode = router.pathname === '/' && !isScrolled && !isMenuOpen;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="font-brand-body min-h-screen bg-[#FBF9F1] text-[#22332B]">
|
|
||||||
<header
|
|
||||||
className={`sticky top-0 z-50 transition-all duration-300 ${
|
|
||||||
isHomeHeroMode
|
|
||||||
? 'border-b border-white/10 bg-[#102118]/62 shadow-[0_18px_50px_rgba(0,0,0,0.18)] backdrop-blur-xl'
|
|
||||||
: 'border-b border-[#1F3A2D]/10 bg-[#FBF9F1]/92 shadow-[0_18px_50px_rgba(20,38,29,0.08)] backdrop-blur-xl'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<div className="mx-auto flex max-w-7xl items-center justify-between gap-4 px-5 py-4 lg:px-8">
|
|
||||||
<Link href="/" className="flex items-center gap-3">
|
|
||||||
<div
|
|
||||||
className={`flex h-12 w-12 items-center justify-center rounded-full border text-sm font-semibold shadow-lg transition-colors duration-300 ${
|
|
||||||
isHomeHeroMode
|
|
||||||
? 'border-white/28 bg-white/12 text-[#F4F1E6] backdrop-blur-md'
|
|
||||||
: 'border-[#9FB06F]/50 bg-[#1F3A2D] text-[#F4F1E6]'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
AD
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p className={`font-brand-display text-lg font-bold transition-colors duration-300 ${isHomeHeroMode ? 'text-[#F4F1E6]' : 'text-[#1F3A2D]'}`}>
|
|
||||||
Alem Desta
|
|
||||||
</p>
|
|
||||||
<p className={`text-xs uppercase tracking-[0.28em] transition-colors duration-300 ${isHomeHeroMode ? 'text-white/60' : 'text-[#72816F]'}`}>
|
|
||||||
Coffee Export
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
<nav className="hidden items-center gap-1 xl:flex">
|
|
||||||
{navigationItems.map((item) => {
|
|
||||||
const isActive = isNavItemActive(router.pathname, item);
|
|
||||||
const classes = getDesktopNavClasses(isActive, isHomeHeroMode);
|
|
||||||
|
|
||||||
if (!item.children) {
|
|
||||||
return (
|
|
||||||
<Link key={item.href} href={item.href} className={`rounded-full px-4 py-2 text-sm font-medium transition-all duration-300 ${classes}`}>
|
|
||||||
{item.label}
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div key={item.href} className="group relative">
|
|
||||||
<Link href={item.href} className={`inline-flex items-center gap-2 rounded-full px-4 py-2 text-sm font-medium transition-all duration-300 ${classes}`}>
|
|
||||||
{item.label}
|
|
||||||
<BaseIcon path={mdiChevronDown} className="transition-transform duration-300 group-hover:rotate-180 group-focus-within:rotate-180" />
|
|
||||||
</Link>
|
|
||||||
<div
|
|
||||||
className={`invisible absolute left-0 top-full mt-3 w-[22rem] translate-y-2 rounded-[1.75rem] border p-3 opacity-0 transition-all duration-300 group-hover:visible group-hover:translate-y-0 group-hover:opacity-100 group-hover:pointer-events-auto group-focus-within:visible group-focus-within:translate-y-0 group-focus-within:opacity-100 ${getDesktopDropdownClasses(
|
|
||||||
isHomeHeroMode,
|
|
||||||
)}`}
|
|
||||||
>
|
|
||||||
{item.children.map((child) => (
|
|
||||||
<Link
|
|
||||||
key={child.href}
|
|
||||||
href={child.href}
|
|
||||||
className={`block rounded-[1.25rem] px-4 py-3 transition-all duration-300 ${getDesktopDropdownItemClasses(
|
|
||||||
router.pathname,
|
|
||||||
child,
|
|
||||||
isHomeHeroMode,
|
|
||||||
)}`}
|
|
||||||
>
|
|
||||||
<p className="text-sm font-semibold">{child.label}</p>
|
|
||||||
<p className={`mt-1 text-xs leading-6 ${isHomeHeroMode ? 'text-white/58' : 'text-[#68766A]'}`}>{child.description}</p>
|
|
||||||
</Link>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<div className="hidden items-center gap-3 xl:flex">
|
|
||||||
<MarketingButton href="/login" variant={isHomeHeroMode ? 'secondary' : 'ghost'}>
|
|
||||||
Admin Login
|
|
||||||
</MarketingButton>
|
|
||||||
<MarketingButton href="/contact?type=get_a_quote">Get a Quote</MarketingButton>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => setIsMenuOpen((current) => !current)}
|
|
||||||
className={`inline-flex h-12 w-12 items-center justify-center rounded-full border transition-all duration-300 xl:hidden ${
|
|
||||||
isHomeHeroMode
|
|
||||||
? 'border-white/20 bg-white/10 text-[#F4F1E6] backdrop-blur-md'
|
|
||||||
: 'border-[#1F3A2D]/10 bg-white text-[#1F3A2D]'
|
|
||||||
}`}
|
|
||||||
aria-label="Toggle navigation"
|
|
||||||
>
|
|
||||||
<BaseIcon path={isMenuOpen ? mdiClose : mdiMenu} />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{isMenuOpen ? (
|
|
||||||
<div className="border-t border-[#1F3A2D]/10 bg-[#FBF9F1] px-5 py-4 xl:hidden">
|
|
||||||
<div className="mb-4 rounded-2xl border border-[#9FB06F]/30 bg-[#F4F1E6] px-4 py-3 text-sm text-[#5F6C61]">
|
|
||||||
Currently viewing: <span className="font-semibold text-[#1F3A2D]">{currentLabel}</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col gap-2">
|
|
||||||
{navigationItems.map((item) => {
|
|
||||||
if (!item.children) {
|
|
||||||
return (
|
|
||||||
<Link
|
|
||||||
key={item.href}
|
|
||||||
href={item.href}
|
|
||||||
className={`rounded-2xl px-4 py-3 text-sm font-medium ${
|
|
||||||
isNavItemActive(router.pathname, item) ? 'bg-[#1F3A2D] text-[#F4F1E6]' : 'bg-white text-[#1F3A2D]'
|
|
||||||
}`}
|
|
||||||
onClick={() => setIsMenuOpen(false)}
|
|
||||||
>
|
|
||||||
{item.label}
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div key={item.href} className="overflow-hidden rounded-[1.5rem] border border-[#1F3A2D]/10 bg-white">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<Link
|
|
||||||
href={item.href}
|
|
||||||
className={`flex-1 px-4 py-3 text-sm font-medium ${
|
|
||||||
isNavItemActive(router.pathname, item) ? 'text-[#1F3A2D]' : 'text-[#32453A]'
|
|
||||||
}`}
|
|
||||||
onClick={() => setIsMenuOpen(false)}
|
|
||||||
>
|
|
||||||
{item.label}
|
|
||||||
</Link>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => setIsExportMenuOpen((current) => !current)}
|
|
||||||
className="px-4 py-3 text-[#1F3A2D]"
|
|
||||||
aria-label="Toggle export services submenu"
|
|
||||||
>
|
|
||||||
<BaseIcon
|
|
||||||
path={mdiChevronDown}
|
|
||||||
className={`transition-transform duration-300 ${isExportMenuOpen ? 'rotate-180' : ''}`}
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
{isExportMenuOpen ? (
|
|
||||||
<div className="border-t border-[#1F3A2D]/10 bg-[#FBF9F1] p-2">
|
|
||||||
{item.children.map((child) => (
|
|
||||||
<Link
|
|
||||||
key={child.href}
|
|
||||||
href={child.href}
|
|
||||||
className={`block rounded-2xl px-4 py-3 ${
|
|
||||||
isActivePath(router.pathname, child.href) ? 'bg-[#1F3A2D] text-[#F4F1E6]' : 'text-[#1F3A2D]'
|
|
||||||
}`}
|
|
||||||
onClick={() => setIsMenuOpen(false)}
|
|
||||||
>
|
|
||||||
<p className="text-sm font-medium">{child.label}</p>
|
|
||||||
<p className={`mt-1 text-xs leading-6 ${isActivePath(router.pathname, child.href) ? 'text-white/70' : 'text-[#68766A]'}`}>
|
|
||||||
{child.description}
|
|
||||||
</p>
|
|
||||||
</Link>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
<div className="mt-4 grid gap-3 sm:grid-cols-2">
|
|
||||||
<MarketingButton href="/login" variant="ghost">
|
|
||||||
Admin Login
|
|
||||||
</MarketingButton>
|
|
||||||
<MarketingButton href="/contact?type=request_samples">Request Samples</MarketingButton>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<main>{children}</main>
|
|
||||||
|
|
||||||
<section className="border-y border-[#1F3A2D]/10 bg-[#1F3A2D] px-5 py-16 text-white lg:px-8">
|
|
||||||
<div className="mx-auto flex max-w-7xl flex-col gap-10 lg:flex-row lg:items-center lg:justify-between">
|
|
||||||
<div className="max-w-3xl">
|
|
||||||
<p className="text-sm uppercase tracking-[0.35em] text-[#B7C78B]">Ready to source with confidence?</p>
|
|
||||||
<h2 className="font-brand-display mt-4 text-4xl leading-tight text-[#F4F1E6] md:text-5xl">
|
|
||||||
Discover Ethiopian coffee backed by heritage, clear communication, and export discipline.
|
|
||||||
</h2>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col gap-3 sm:flex-row">
|
|
||||||
<MarketingButton href="/contact?type=request_samples">Request Samples</MarketingButton>
|
|
||||||
<MarketingButton href="/contact?type=get_a_quote" variant="secondary">
|
|
||||||
Get a Quote
|
|
||||||
</MarketingButton>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<footer className="bg-[#14261D] px-5 py-16 text-white lg:px-8">
|
|
||||||
<div className="mx-auto grid max-w-7xl gap-10 lg:grid-cols-[1.1fr_0.7fr_0.95fr_1fr]">
|
|
||||||
<div>
|
|
||||||
<p className="font-brand-display text-3xl text-[#F4F1E6]">{brand.name}</p>
|
|
||||||
<p className="mt-4 max-w-xl text-sm leading-7 text-white/72">{brand.tagline}</p>
|
|
||||||
<div className="mt-8 flex flex-wrap gap-3">
|
|
||||||
<MarketingButton href="/contact?type=partner_with_us">Partner With Us</MarketingButton>
|
|
||||||
<MarketingButton href="/login" variant="secondary">
|
|
||||||
Admin Interface
|
|
||||||
</MarketingButton>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<p className="text-sm font-semibold uppercase tracking-[0.28em] text-[#9FB06F]">Main pages</p>
|
|
||||||
<div className="mt-5 grid gap-3">
|
|
||||||
{navigationItems.map((item) => (
|
|
||||||
<Link key={item.href} href={item.href} className="inline-flex items-center gap-2 text-sm text-white/75 transition hover:text-white">
|
|
||||||
<BaseIcon path={mdiArrowRight} className="text-[#9FB06F]" />
|
|
||||||
{item.label}
|
|
||||||
</Link>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<p className="text-sm font-semibold uppercase tracking-[0.28em] text-[#9FB06F]">Export services</p>
|
|
||||||
<div className="mt-5 grid gap-3">
|
|
||||||
{exportServiceItems.map((item) => (
|
|
||||||
<Link key={item.href} href={item.href} className="inline-flex items-center gap-2 text-sm text-white/75 transition hover:text-white">
|
|
||||||
<BaseIcon path={mdiArrowRight} className="text-[#9FB06F]" />
|
|
||||||
{item.label}
|
|
||||||
</Link>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<div className="mt-8 space-y-2 text-sm text-white/75">
|
|
||||||
<p>{brand.address}</p>
|
|
||||||
<p>{brand.email}</p>
|
|
||||||
{brand.phones.map((phone) => (
|
|
||||||
<p key={phone}>{phone}</p>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<p className="text-sm font-semibold uppercase tracking-[0.28em] text-[#9FB06F]">Newsletter subscription</p>
|
|
||||||
<p className="mt-4 text-sm leading-7 text-white/72">
|
|
||||||
Receive market-ready updates, sourcing availability, and company news tailored for international buyers.
|
|
||||||
</p>
|
|
||||||
<div className="mt-5">
|
|
||||||
<NewsletterForm compact source="home_footer" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
eyebrow: string;
|
|
||||||
title: string;
|
|
||||||
description: string;
|
|
||||||
image: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function MarketingPageHero({ eyebrow, title, description, image }: Props) {
|
|
||||||
return (
|
|
||||||
<section className="px-5 pb-8 pt-12 lg:px-8 lg:pb-12 lg:pt-16">
|
|
||||||
<div className="mx-auto grid max-w-7xl gap-8 overflow-hidden rounded-[2rem] bg-[#F4F1E6] p-6 shadow-[0_28px_90px_rgba(31,58,45,0.08)] lg:grid-cols-[1.05fr_0.95fr] lg:p-10">
|
|
||||||
<div className="flex flex-col justify-center">
|
|
||||||
<p className="text-sm font-semibold uppercase tracking-[0.35em] text-[#9FB06F]">{eyebrow}</p>
|
|
||||||
<h1 className="font-brand-display mt-4 text-4xl leading-tight text-[#1F3A2D] md:text-6xl">{title}</h1>
|
|
||||||
<p className="mt-6 max-w-2xl text-base leading-8 text-[#556257]">{description}</p>
|
|
||||||
</div>
|
|
||||||
<div className="relative min-h-[320px] overflow-hidden rounded-[1.75rem]">
|
|
||||||
<img className="absolute inset-0 h-full w-full object-cover saturate-[0.88]" loading="eager" src={image} alt={title} />
|
|
||||||
<div className="absolute inset-0 bg-[linear-gradient(160deg,rgba(16,33,24,0.08),rgba(36,63,48,0.22),rgba(16,33,24,0.42))]" />
|
|
||||||
<div className="absolute inset-0 bg-gradient-to-t from-[#102118]/54 via-transparent to-transparent" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,32 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
eyebrow?: string;
|
|
||||||
title: string;
|
|
||||||
description?: string;
|
|
||||||
children?: React.ReactNode;
|
|
||||||
className?: string;
|
|
||||||
centered?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function MarketingSection({
|
|
||||||
eyebrow,
|
|
||||||
title,
|
|
||||||
description,
|
|
||||||
children,
|
|
||||||
className = '',
|
|
||||||
centered = false,
|
|
||||||
}: Props) {
|
|
||||||
return (
|
|
||||||
<section className={`px-5 py-16 lg:px-8 lg:py-24 ${className}`}>
|
|
||||||
<div className="mx-auto max-w-7xl">
|
|
||||||
<div className={centered ? 'mx-auto max-w-3xl text-center' : 'max-w-3xl'}>
|
|
||||||
{eyebrow ? <p className="text-sm font-semibold uppercase tracking-[0.35em] text-[#9FB06F]">{eyebrow}</p> : null}
|
|
||||||
<h2 className="font-brand-display mt-4 text-4xl leading-tight text-[#1F3A2D] md:text-5xl">{title}</h2>
|
|
||||||
{description ? <p className="mt-5 text-base leading-8 text-[#556257]">{description}</p> : null}
|
|
||||||
</div>
|
|
||||||
{children ? <div className="mt-12">{children}</div> : null}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,25 +0,0 @@
|
|||||||
import Head from 'next/head';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import { brand } from './marketingData';
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
title: string;
|
|
||||||
description: string;
|
|
||||||
keywords?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function MarketingSeo({ title, description, keywords }: Props) {
|
|
||||||
const fullTitle = `${title} | ${brand.name}`;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Head>
|
|
||||||
<title>{fullTitle}</title>
|
|
||||||
<meta name="description" content={description} />
|
|
||||||
<meta
|
|
||||||
name="keywords"
|
|
||||||
content={keywords || 'Ethiopian coffee export, specialty coffee, green coffee beans, single origin coffee'}
|
|
||||||
/>
|
|
||||||
</Head>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,119 +0,0 @@
|
|||||||
import axios from 'axios';
|
|
||||||
import React, { useState } from 'react';
|
|
||||||
|
|
||||||
import MarketingButton from './MarketingButton';
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
source?: string;
|
|
||||||
compact?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
type NewsletterState = {
|
|
||||||
email: string;
|
|
||||||
full_name: string;
|
|
||||||
company_name: string;
|
|
||||||
country: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const initialState: NewsletterState = {
|
|
||||||
email: '',
|
|
||||||
full_name: '',
|
|
||||||
company_name: '',
|
|
||||||
country: '',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function NewsletterForm({ source = 'home_footer', compact = false }: Props) {
|
|
||||||
const [form, setForm] = useState<NewsletterState>(initialState);
|
|
||||||
const [errorMessage, setErrorMessage] = useState('');
|
|
||||||
const [successMessage, setSuccessMessage] = useState('');
|
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
||||||
|
|
||||||
const onChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
setForm((current) => ({
|
|
||||||
...current,
|
|
||||||
[event.target.name]: event.target.value,
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
const onSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
|
|
||||||
event.preventDefault();
|
|
||||||
setErrorMessage('');
|
|
||||||
setSuccessMessage('');
|
|
||||||
|
|
||||||
if (!form.email.trim()) {
|
|
||||||
setErrorMessage('Email is required.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setIsSubmitting(true);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await axios.post('/contact-form/newsletter', {
|
|
||||||
data: {
|
|
||||||
...form,
|
|
||||||
source,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
setSuccessMessage('Thank you. You have been added to our coffee export updates list.');
|
|
||||||
setForm(initialState);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Newsletter subscription failed', error);
|
|
||||||
setErrorMessage('Subscription could not be completed right now. Please try again shortly.');
|
|
||||||
} finally {
|
|
||||||
setIsSubmitting(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<form className="space-y-3" onSubmit={onSubmit}>
|
|
||||||
{!compact && (
|
|
||||||
<div className="grid gap-3 md:grid-cols-2">
|
|
||||||
<input
|
|
||||||
className="h-12 rounded-2xl border border-white/15 bg-white/10 px-4 text-sm text-white placeholder:text-white/55 focus:outline-none focus:ring-2 focus:ring-[#9FB06F]"
|
|
||||||
name="full_name"
|
|
||||||
placeholder="Name"
|
|
||||||
value={form.full_name}
|
|
||||||
onChange={onChange}
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
className="h-12 rounded-2xl border border-white/15 bg-white/10 px-4 text-sm text-white placeholder:text-white/55 focus:outline-none focus:ring-2 focus:ring-[#9FB06F]"
|
|
||||||
name="company_name"
|
|
||||||
placeholder="Company"
|
|
||||||
value={form.company_name}
|
|
||||||
onChange={onChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className={`grid gap-3 ${compact ? 'md:grid-cols-[minmax(0,1fr)_auto]' : 'md:grid-cols-[minmax(0,1fr)_minmax(0,1fr)]'}`}>
|
|
||||||
<input
|
|
||||||
className="h-12 rounded-2xl border border-white/15 bg-white/10 px-4 text-sm text-white placeholder:text-white/55 focus:outline-none focus:ring-2 focus:ring-[#9FB06F]"
|
|
||||||
name="email"
|
|
||||||
placeholder="Email address"
|
|
||||||
type="email"
|
|
||||||
value={form.email}
|
|
||||||
onChange={onChange}
|
|
||||||
/>
|
|
||||||
{compact ? (
|
|
||||||
<MarketingButton className="w-full md:w-auto" type="submit">
|
|
||||||
{isSubmitting ? 'Subscribing...' : 'Subscribe'}
|
|
||||||
</MarketingButton>
|
|
||||||
) : (
|
|
||||||
<input
|
|
||||||
className="h-12 rounded-2xl border border-white/15 bg-white/10 px-4 text-sm text-white placeholder:text-white/55 focus:outline-none focus:ring-2 focus:ring-[#9FB06F]"
|
|
||||||
name="country"
|
|
||||||
placeholder="Country"
|
|
||||||
value={form.country}
|
|
||||||
onChange={onChange}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{!compact && (
|
|
||||||
<MarketingButton className="w-full md:w-auto" type="submit">
|
|
||||||
{isSubmitting ? 'Subscribing...' : 'Subscribe to updates'}
|
|
||||||
</MarketingButton>
|
|
||||||
)}
|
|
||||||
{errorMessage ? <p className="text-sm text-[#f8caca]">{errorMessage}</p> : null}
|
|
||||||
{successMessage ? <p className="text-sm text-[#d9c08e]">{successMessage}</p> : null}
|
|
||||||
</form>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,276 +0,0 @@
|
|||||||
export const brand = {
|
|
||||||
name: 'Alem Desta Coffee Export',
|
|
||||||
tagline: 'Premium Ethiopian green coffee sourced with heritage, integrity, and export discipline.',
|
|
||||||
email: 'info@alemdesta.com',
|
|
||||||
phones: ['+251 930105914', '+251 904186868'],
|
|
||||||
address: 'Kirkos Sub-city, Wereda 01, Addis Ababa, Ethiopia',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const exportServiceItems = [
|
|
||||||
{
|
|
||||||
href: '/coffee-origins',
|
|
||||||
label: 'Coffee Origins',
|
|
||||||
description: 'Single-origin coffees from iconic Ethiopian regions with distinct altitude, profile, and terroir.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
href: '/quality-control',
|
|
||||||
label: 'Quality Control',
|
|
||||||
description: 'Disciplined cherry selection, moisture monitoring, and cupping protocols before export.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
href: '/sustainability',
|
|
||||||
label: 'Sustainability',
|
|
||||||
description: 'Long-term sourcing built around farmer respect, environmental care, and transparent relationships.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
href: '/logistics-export',
|
|
||||||
label: 'Logistics & Export',
|
|
||||||
description: 'Documentation, shipment coordination, and responsive communication from Addis Ababa to destination.',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const navigationItems = [
|
|
||||||
{ href: '/', label: 'Home' },
|
|
||||||
{ href: '/about', label: 'About' },
|
|
||||||
{ href: '/export-services', label: 'Export Services', children: exportServiceItems },
|
|
||||||
{ href: '/contact', label: 'Contact' },
|
|
||||||
];
|
|
||||||
|
|
||||||
export const featureHighlights = [
|
|
||||||
{
|
|
||||||
title: 'Traceability & Transparency',
|
|
||||||
description:
|
|
||||||
'Every shipment is sourced with documented origin details, responsible handling, and buyer-ready clarity from farm gate to export.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Premium Quality Control',
|
|
||||||
description:
|
|
||||||
'Careful cherry selection, moisture monitoring, cupping review, and export preparation protect cup quality at every stage.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Direct Farmer Partnerships',
|
|
||||||
description:
|
|
||||||
'We build relationships with skilled producers and washing stations to secure consistency, trust, and long-term value.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Sustainable Sourcing',
|
|
||||||
description:
|
|
||||||
'Commercial success is paired with responsible sourcing practices that respect communities, land, and future harvests.',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const galleryItems = [
|
|
||||||
{
|
|
||||||
title: 'Green coffee landscapes',
|
|
||||||
caption: 'Highland farms and careful cultivation create the foundation for exceptional green coffee lots.',
|
|
||||||
image:
|
|
||||||
'https://images.unsplash.com/photo-1447933601403-0c6688de566e?auto=format&fit=crop&w=1200&q=80',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Washing station discipline',
|
|
||||||
caption: 'Lot separation, drying control, and station handling protect specialty green coffee quality before export.',
|
|
||||||
image:
|
|
||||||
'https://images.unsplash.com/photo-1495474472287-4d71bcdd2085?auto=format&fit=crop&w=1200&q=80',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Lot evaluation',
|
|
||||||
caption: 'Cupping, moisture review, and sample assessment help buyers select green coffee with confidence.',
|
|
||||||
image:
|
|
||||||
'https://images.unsplash.com/photo-1511920170033-f8396924c348?auto=format&fit=crop&w=1200&q=80',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Export-ready preparation',
|
|
||||||
caption: 'Packaging, documentation, and shipment coordination move export-ready green coffee toward global buyers.',
|
|
||||||
image:
|
|
||||||
'https://images.unsplash.com/photo-1520607162513-77705c0f0d4a?auto=format&fit=crop&w=1200&q=80',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const founderStory = [
|
|
||||||
'Alem Desta Coffee Export was shaped by a life rooted in Ethiopian coffee culture, where coffee is more than a crop; it is hospitality, identity, and daily ritual. Growing up with that tradition created a deep respect for the people, landscapes, and craftsmanship behind every cup.',
|
|
||||||
'Family tradition made coffee familiar from an early age, but global experience expanded the vision. Time spent in the United States — including Chicago, San Francisco, and the DMV area — offered a firsthand understanding of how international buyers evaluate quality, consistency, communication, and professionalism.',
|
|
||||||
'Returning to Ethiopia brought that perspective back to origin. It also created the opportunity to learn closely from a brother already active in coffee exporting, translating heritage into disciplined export practice, relationship building, and an appreciation for what buyers genuinely need from a trusted supplier.',
|
|
||||||
'Today the business stands at the intersection of culture, global exposure, and commercial responsibility: honoring Ethiopian roots while delivering premium coffee with clear communication, traceability, and long-term partnership in mind.',
|
|
||||||
];
|
|
||||||
|
|
||||||
export const philosophyPillars = [
|
|
||||||
{
|
|
||||||
title: 'Altitude',
|
|
||||||
description: 'High-altitude growing environments support clarity, floral aromatics, and refined complexity.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Soil',
|
|
||||||
description: 'Rich natural soils nourish coffee trees slowly and help create layered flavor expression.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Climate',
|
|
||||||
description: 'Balanced rainfall, sunlight, and cool nights allow cherries to mature with patience.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Human Craftsmanship',
|
|
||||||
description: 'Excellence depends on farmers, processors, cuppers, and exporters applying skill at every step.',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const values = [
|
|
||||||
{
|
|
||||||
title: 'Integrity',
|
|
||||||
description: 'We communicate honestly, document carefully, and build trust through dependable execution.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Culture & Heritage',
|
|
||||||
description: 'Our work is grounded in Ethiopian coffee tradition and respect for origin.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Quality Excellence',
|
|
||||||
description: 'We pursue careful selection, disciplined processing, and export readiness without compromise.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Partnership',
|
|
||||||
description: 'We value long-term relationships with farmers, washing stations, and international buyers.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Responsibility',
|
|
||||||
description: 'Commercial growth should strengthen communities and support ethical business practices.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Sustainability',
|
|
||||||
description: 'Responsible sourcing protects both the environment and the future of coffee farming.',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const principles = [
|
|
||||||
'Direct sourcing',
|
|
||||||
'Full traceability',
|
|
||||||
'Compliance with international standards',
|
|
||||||
'Long-term relationships',
|
|
||||||
'Continuous improvement',
|
|
||||||
];
|
|
||||||
|
|
||||||
export const origins = [
|
|
||||||
{
|
|
||||||
name: 'Yirgacheffe',
|
|
||||||
profile: 'Floral, citrus, bright acidity',
|
|
||||||
altitude: '1900–2200 MASL',
|
|
||||||
rainfall: 'Balanced seasonal rainfall',
|
|
||||||
image:
|
|
||||||
'https://images.unsplash.com/photo-1461988320302-91bde64fc8e4?auto=format&fit=crop&w=900&q=80',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Guji',
|
|
||||||
profile: 'Fruity, vibrant sweetness',
|
|
||||||
altitude: '1800–2200 MASL',
|
|
||||||
rainfall: 'Reliable mountain rainfall',
|
|
||||||
image:
|
|
||||||
'https://images.unsplash.com/photo-1509042239860-f550ce710b93?auto=format&fit=crop&w=900&q=80',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Sidama',
|
|
||||||
profile: 'Balanced, complex flavor',
|
|
||||||
altitude: '1700–2200 MASL',
|
|
||||||
rainfall: 'Rich rainfall with cool nights',
|
|
||||||
image:
|
|
||||||
'https://images.unsplash.com/photo-1442512595331-e89e73853f31?auto=format&fit=crop&w=900&q=80',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Harrar',
|
|
||||||
profile: 'Bold, winey, spicy',
|
|
||||||
altitude: '1500–2100 MASL',
|
|
||||||
rainfall: 'Drier climate with intense character',
|
|
||||||
image:
|
|
||||||
'https://images.unsplash.com/photo-1455470956270-4cbb357f60f6?auto=format&fit=crop&w=900&q=80',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Limu & Jimma',
|
|
||||||
profile: 'Chocolatey, smooth',
|
|
||||||
altitude: '1400–2100 MASL',
|
|
||||||
rainfall: 'Steady seasonal rainfall',
|
|
||||||
image:
|
|
||||||
'https://images.unsplash.com/photo-1494314671902-399b18174975?auto=format&fit=crop&w=900&q=80',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const qualityProcesses = [
|
|
||||||
{
|
|
||||||
title: 'Washed Process',
|
|
||||||
highlight: 'Clean, bright coffee profile',
|
|
||||||
steps: ['Selective harvesting', 'Pulping', 'Fermentation', 'Washing', 'Drying', 'Milling'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Natural Process',
|
|
||||||
highlight: 'Sun-dried cherries develop fruity, bold flavors with patient drying.',
|
|
||||||
steps: ['Selective harvesting', 'Whole cherry drying', 'Slow turning', 'Moisture stabilization', 'Hulling'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Honey Process',
|
|
||||||
highlight: 'A semi-washed style balancing sweetness, texture, and acidity.',
|
|
||||||
steps: ['Selective harvesting', 'Depulping', 'Mucilage retention', 'Controlled drying', 'Milling'],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const qualityMetrics = [
|
|
||||||
'Moisture control: 10–12%',
|
|
||||||
'Cupping evaluation of aroma',
|
|
||||||
'Body and mouthfeel review',
|
|
||||||
'Acidity balance and clarity',
|
|
||||||
'Aftertaste consistency',
|
|
||||||
];
|
|
||||||
|
|
||||||
export const exportServices = [
|
|
||||||
'FOB Djibouti export system managed with professional coordination',
|
|
||||||
'Documentation handled internally for smoother buyer communication',
|
|
||||||
'Reliable logistics chain from Addis Ababa to Djibouti and onward to global markets',
|
|
||||||
'Flexible order volumes for specialty buyers, roasters, and wholesale partners',
|
|
||||||
'Timely delivery expectations supported by strong communication and planning',
|
|
||||||
];
|
|
||||||
|
|
||||||
export const sustainabilityPoints = [
|
|
||||||
'Fair payment practices that respect producer effort and quality',
|
|
||||||
'Ethical sourcing grounded in long-term supplier relationships',
|
|
||||||
'Environmental responsibility in cultivation and processing decisions',
|
|
||||||
'Community development engagement where coffee value begins',
|
|
||||||
];
|
|
||||||
|
|
||||||
export const testimonials = [
|
|
||||||
{
|
|
||||||
quote:
|
|
||||||
'A disciplined, origin-driven Ethiopian exporter with the kind of communication international buyers appreciate.',
|
|
||||||
author: 'Testimonial placeholder — Specialty roasting partner',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
quote:
|
|
||||||
'The combination of traceability, cup quality, and professionalism signals strong export potential.',
|
|
||||||
author: 'Testimonial placeholder — Green coffee importer',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const buyerTypes = [
|
|
||||||
{ value: 'roaster', label: 'Roaster' },
|
|
||||||
{ value: 'importer', label: 'Importer' },
|
|
||||||
{ value: 'distributor', label: 'Distributor' },
|
|
||||||
{ value: 'specialty_buyer', label: 'Specialty buyer' },
|
|
||||||
{ value: 'wholesaler', label: 'Wholesaler' },
|
|
||||||
{ value: 'other', label: 'Other' },
|
|
||||||
];
|
|
||||||
|
|
||||||
export const inquiryTypes = [
|
|
||||||
{ value: 'request_samples', label: 'Request Samples' },
|
|
||||||
{ value: 'get_a_quote', label: 'Get a Quote' },
|
|
||||||
{ value: 'partner_with_us', label: 'Partner With Us' },
|
|
||||||
{ value: 'contact_message', label: 'General Inquiry' },
|
|
||||||
];
|
|
||||||
|
|
||||||
export const incoterms = [
|
|
||||||
{ value: 'fob', label: 'FOB' },
|
|
||||||
{ value: 'cif', label: 'CIF' },
|
|
||||||
{ value: 'cfr', label: 'CFR' },
|
|
||||||
{ value: 'exw', label: 'EXW' },
|
|
||||||
{ value: 'dap', label: 'DAP' },
|
|
||||||
{ value: 'other', label: 'Other' },
|
|
||||||
];
|
|
||||||
|
|
||||||
export const contactMethods = [
|
|
||||||
{ value: 'email', label: 'Email' },
|
|
||||||
{ value: 'phone', label: 'Phone' },
|
|
||||||
{ value: 'whatsapp', label: 'WhatsApp' },
|
|
||||||
];
|
|
||||||
@ -62,62 +62,3 @@ font-family: 'Ubuntu', sans-serif !important;
|
|||||||
.introjs-prevbutton{
|
.introjs-prevbutton{
|
||||||
@apply bg-transparent border border-pastelBrownTheme-buttonColor text-pastelBrownTheme-buttonColor !important;
|
@apply bg-transparent border border-pastelBrownTheme-buttonColor text-pastelBrownTheme-buttonColor !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=Playfair+Display:wght@600;700;800&display=swap');
|
|
||||||
|
|
||||||
:root {
|
|
||||||
--coffee-brown: #1F3A2D;
|
|
||||||
--coffee-beige: #F4F1E6;
|
|
||||||
--coffee-gold: #9FB06F;
|
|
||||||
}
|
|
||||||
|
|
||||||
html {
|
|
||||||
scroll-behavior: smooth;
|
|
||||||
}
|
|
||||||
|
|
||||||
.font-brand-display {
|
|
||||||
font-family: 'Playfair Display', Georgia, serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
.font-brand-body {
|
|
||||||
font-family: 'Inter', 'Ubuntu', sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
.coffee-input {
|
|
||||||
width: 100%;
|
|
||||||
height: 3.25rem;
|
|
||||||
border-radius: 1rem;
|
|
||||||
border: 1px solid rgba(31, 58, 45, 0.14);
|
|
||||||
background: #F8FAF4;
|
|
||||||
padding: 0 1rem;
|
|
||||||
font-size: 0.95rem;
|
|
||||||
color: #22332B;
|
|
||||||
transition: border-color 0.2s ease, box-shadow 0.2s ease, background 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.coffee-input:focus {
|
|
||||||
outline: none;
|
|
||||||
border-color: rgba(159, 176, 111, 0.82);
|
|
||||||
box-shadow: 0 0 0 3px rgba(159, 176, 111, 0.18);
|
|
||||||
background: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.coffee-input::placeholder {
|
|
||||||
color: #869488;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes coffeeFadeIn {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(14px);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.coffee-fade-in {
|
|
||||||
animation: coffeeFadeIn 0.8s ease both;
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import React, { ReactNode, useEffect, useState } from 'react'
|
import React, { ReactNode, useEffect } from 'react'
|
||||||
|
import { useState } from 'react'
|
||||||
import jwt from 'jsonwebtoken';
|
import jwt from 'jsonwebtoken';
|
||||||
import { mdiForwardburger, mdiBackburger, mdiMenu } from '@mdi/js'
|
import { mdiForwardburger, mdiBackburger, mdiMenu } from '@mdi/js'
|
||||||
import menuAside from '../menuAside'
|
import menuAside from '../menuAside'
|
||||||
|
|||||||
@ -1,58 +0,0 @@
|
|||||||
import type { ReactElement } from 'react';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import MarketingLayout from '../components/marketing/MarketingLayout';
|
|
||||||
import MarketingPageHero from '../components/marketing/MarketingPageHero';
|
|
||||||
import MarketingSeo from '../components/marketing/MarketingSeo';
|
|
||||||
import MarketingSection from '../components/marketing/MarketingSection';
|
|
||||||
import { founderStory } from '../components/marketing/marketingData';
|
|
||||||
import LayoutGuest from '../layouts/Guest';
|
|
||||||
|
|
||||||
export default function AboutPage() {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<MarketingSeo
|
|
||||||
title="About Alem Desta Coffee Export"
|
|
||||||
description="Founder story, Ethiopian coffee heritage, U.S. market exposure, and the roots behind Alem Desta Coffee Export."
|
|
||||||
/>
|
|
||||||
<MarketingLayout>
|
|
||||||
<MarketingPageHero
|
|
||||||
eyebrow="About us"
|
|
||||||
title="A heritage-led coffee export business shaped by global perspective"
|
|
||||||
description="Our story combines Ethiopian coffee culture, family tradition, U.S. market exposure, and practical export learning grounded in real relationships."
|
|
||||||
image="https://images.unsplash.com/photo-1495474472287-4d71bcdd2085?auto=format&fit=crop&w=1400&q=80"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<MarketingSection
|
|
||||||
eyebrow="Founder story"
|
|
||||||
title="From Ethiopian coffee culture to global buyer understanding"
|
|
||||||
description="The company story is personal, emotional, and practical — exactly the mix international buyers often trust most when choosing who to work with."
|
|
||||||
>
|
|
||||||
<div className="grid gap-6 lg:grid-cols-[0.8fr_1.2fr]">
|
|
||||||
<div className="rounded-[2rem] bg-[#1F3A2D] p-8 text-white shadow-[0_24px_80px_rgba(20,38,29,0.14)]">
|
|
||||||
<p className="text-sm uppercase tracking-[0.32em] text-[#B7C78B]">What shapes the company</p>
|
|
||||||
<ul className="mt-6 space-y-4 text-sm leading-7 text-white/78">
|
|
||||||
<li>• Ethiopian coffee culture upbringing</li>
|
|
||||||
<li>• Family coffee tradition</li>
|
|
||||||
<li>• U.S. experience in Chicago, San Francisco, and the DMV</li>
|
|
||||||
<li>• Return to Ethiopia with renewed business purpose</li>
|
|
||||||
<li>• Export learning through close family mentorship</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-6">
|
|
||||||
{founderStory.map((paragraph) => (
|
|
||||||
<div key={paragraph} className="rounded-[1.75rem] border border-[#1F3A2D]/8 bg-white p-7 shadow-[0_18px_60px_rgba(20,38,29,0.06)]">
|
|
||||||
<p className="text-base leading-8 text-[#556257]">{paragraph}</p>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</MarketingSection>
|
|
||||||
</MarketingLayout>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
AboutPage.getLayout = function getLayout(page: ReactElement) {
|
|
||||||
return <LayoutGuest>{page}</LayoutGuest>;
|
|
||||||
};
|
|
||||||
@ -1,55 +0,0 @@
|
|||||||
import type { ReactElement } from 'react';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import MarketingLayout from '../components/marketing/MarketingLayout';
|
|
||||||
import MarketingPageHero from '../components/marketing/MarketingPageHero';
|
|
||||||
import MarketingSeo from '../components/marketing/MarketingSeo';
|
|
||||||
import MarketingSection from '../components/marketing/MarketingSection';
|
|
||||||
import { origins } from '../components/marketing/marketingData';
|
|
||||||
import LayoutGuest from '../layouts/Guest';
|
|
||||||
|
|
||||||
export default function CoffeeOriginsPage() {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<MarketingSeo
|
|
||||||
title="Coffee Origins"
|
|
||||||
description="Explore Ethiopian coffee regions including Yirgacheffe, Guji, Sidama, Harrar, Limu, and Jimma."
|
|
||||||
/>
|
|
||||||
<MarketingLayout>
|
|
||||||
<MarketingPageHero
|
|
||||||
eyebrow="Coffee origins"
|
|
||||||
title="Regional character that gives Ethiopian coffee its global distinction"
|
|
||||||
description="Each origin tells a different flavor story, giving international buyers a practical way to align sourcing decisions with roast goals and customer preferences."
|
|
||||||
image="https://images.unsplash.com/photo-1455470956270-4cbb357f60f6?auto=format&fit=crop&w=1400&q=80"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<MarketingSection
|
|
||||||
eyebrow="Origin portfolio"
|
|
||||||
title="A curated overview of key coffee-producing regions"
|
|
||||||
description="These region cards provide a premium, buyer-friendly snapshot of profile, altitude, and growing context."
|
|
||||||
>
|
|
||||||
<div className="grid gap-6 lg:grid-cols-2 xl:grid-cols-3">
|
|
||||||
{origins.map((item) => (
|
|
||||||
<article key={item.name} className="overflow-hidden rounded-[2rem] bg-white shadow-[0_24px_70px_rgba(20,38,29,0.08)]">
|
|
||||||
<div className="relative">
|
|
||||||
<img className="h-64 w-full object-cover saturate-[0.9]" loading="lazy" src={item.image} alt={item.name} />
|
|
||||||
<div className="absolute inset-0 bg-[linear-gradient(180deg,rgba(18,38,29,0.05),rgba(18,38,29,0),rgba(18,38,29,0.25))]" />
|
|
||||||
</div>
|
|
||||||
<div className="p-6">
|
|
||||||
<p className="text-sm uppercase tracking-[0.28em] text-[#9FB06F]">{item.altitude}</p>
|
|
||||||
<h2 className="font-brand-display mt-3 text-3xl text-[#1F3A2D]">{item.name}</h2>
|
|
||||||
<p className="mt-4 text-sm leading-7 text-[#556257]">{item.profile}</p>
|
|
||||||
<div className="mt-5 rounded-2xl bg-[#F4F1E6] px-4 py-3 text-sm text-[#556257]">Rainfall: {item.rainfall}</div>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</MarketingSection>
|
|
||||||
</MarketingLayout>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
CoffeeOriginsPage.getLayout = function getLayout(page: ReactElement) {
|
|
||||||
return <LayoutGuest>{page}</LayoutGuest>;
|
|
||||||
};
|
|
||||||
@ -1,55 +0,0 @@
|
|||||||
import { mdiTerrain, mdiWeatherPartlyCloudy, mdiSprout, mdiHumanGreeting } from '@mdi/js';
|
|
||||||
import type { ReactElement } from 'react';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import BaseIcon from '../components/BaseIcon';
|
|
||||||
import MarketingLayout from '../components/marketing/MarketingLayout';
|
|
||||||
import MarketingPageHero from '../components/marketing/MarketingPageHero';
|
|
||||||
import MarketingSeo from '../components/marketing/MarketingSeo';
|
|
||||||
import MarketingSection from '../components/marketing/MarketingSection';
|
|
||||||
import { philosophyPillars } from '../components/marketing/marketingData';
|
|
||||||
import LayoutGuest from '../layouts/Guest';
|
|
||||||
|
|
||||||
const icons = [mdiTerrain, mdiSprout, mdiWeatherPartlyCloudy, mdiHumanGreeting];
|
|
||||||
|
|
||||||
export default function CoffeePhilosophyPage() {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<MarketingSeo
|
|
||||||
title="Coffee Philosophy"
|
|
||||||
description="Coffee excellence begins at origin — patience, altitude, climate, soil, and craftsmanship define premium Ethiopian coffee."
|
|
||||||
/>
|
|
||||||
<MarketingLayout>
|
|
||||||
<MarketingPageHero
|
|
||||||
eyebrow="Coffee philosophy"
|
|
||||||
title="Coffee Excellence Begins at Origin"
|
|
||||||
description="Coffee is not industrial. It is crafted — by land, weather, skilled hands, and the patience required to let quality mature naturally."
|
|
||||||
image="https://images.unsplash.com/photo-1509042239860-f550ce710b93?auto=format&fit=crop&w=1400&q=80"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<MarketingSection
|
|
||||||
eyebrow="Our belief"
|
|
||||||
title="Great coffee is the result of nature and human discipline working together"
|
|
||||||
description="This page articulates a premium sourcing philosophy buyers can feel: Ethiopian coffee is a crafted product, not a commodity shortcut."
|
|
||||||
centered
|
|
||||||
>
|
|
||||||
<div className="grid gap-6 md:grid-cols-2 xl:grid-cols-4">
|
|
||||||
{philosophyPillars.map((item, index) => (
|
|
||||||
<div key={item.title} className="rounded-[1.75rem] border border-[#1F3A2D]/8 bg-white p-7 shadow-[0_20px_65px_rgba(20,38,29,0.06)]">
|
|
||||||
<div className="flex h-12 w-12 items-center justify-center rounded-2xl bg-[#F4F1E6] text-[#1F3A2D]">
|
|
||||||
<BaseIcon path={icons[index]} />
|
|
||||||
</div>
|
|
||||||
<h3 className="font-brand-display mt-5 text-2xl text-[#1F3A2D]">{item.title}</h3>
|
|
||||||
<p className="mt-4 text-sm leading-7 text-[#556257]">{item.description}</p>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</MarketingSection>
|
|
||||||
</MarketingLayout>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
CoffeePhilosophyPage.getLayout = function getLayout(page: ReactElement) {
|
|
||||||
return <LayoutGuest>{page}</LayoutGuest>;
|
|
||||||
};
|
|
||||||
@ -1,102 +0,0 @@
|
|||||||
import { mdiEmailOutline, mdiMapMarkerOutline, mdiPhoneOutline } from '@mdi/js';
|
|
||||||
import type { ReactElement } from 'react';
|
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import BaseIcon from '../components/BaseIcon';
|
|
||||||
import InquiryForm from '../components/marketing/InquiryForm';
|
|
||||||
import MarketingButton from '../components/marketing/MarketingButton';
|
|
||||||
import MarketingLayout from '../components/marketing/MarketingLayout';
|
|
||||||
import MarketingPageHero from '../components/marketing/MarketingPageHero';
|
|
||||||
import MarketingSeo from '../components/marketing/MarketingSeo';
|
|
||||||
import MarketingSection from '../components/marketing/MarketingSection';
|
|
||||||
import { brand } from '../components/marketing/marketingData';
|
|
||||||
import LayoutGuest from '../layouts/Guest';
|
|
||||||
|
|
||||||
const inquiryLinks = [
|
|
||||||
{ href: '/contact?type=request_samples', label: 'Request Samples' },
|
|
||||||
{ href: '/contact?type=get_a_quote', label: 'Get a Quote' },
|
|
||||||
{ href: '/contact?type=partner_with_us', label: 'Partner With Us' },
|
|
||||||
];
|
|
||||||
|
|
||||||
export default function ContactPage() {
|
|
||||||
const router = useRouter();
|
|
||||||
const initialType = typeof router.query.type === 'string' ? router.query.type : undefined;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<MarketingSeo
|
|
||||||
title="Contact"
|
|
||||||
description="Contact Alem Desta Coffee Export in Addis Ababa and submit sample requests, quote requests, or partnership inquiries."
|
|
||||||
/>
|
|
||||||
<MarketingLayout>
|
|
||||||
<MarketingPageHero
|
|
||||||
eyebrow="Contact"
|
|
||||||
title="Start a confident buying conversation"
|
|
||||||
description="The contact experience is designed as a real workflow: choose your inquiry type, submit details, and route the request directly into the admin inquiry queue."
|
|
||||||
image="https://images.unsplash.com/photo-1494314671902-399b18174975?auto=format&fit=crop&w=1400&q=80"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<MarketingSection
|
|
||||||
eyebrow="How to engage"
|
|
||||||
title="Choose the right buyer action and share your needs"
|
|
||||||
description="Sample requests, quotes, and partnership conversations all flow through one elegant form so your team can respond quickly and consistently."
|
|
||||||
>
|
|
||||||
<div className="flex flex-wrap gap-3">
|
|
||||||
{inquiryLinks.map((item) => (
|
|
||||||
<MarketingButton key={item.href} href={item.href} variant={initialType === item.href.split('=')[1] ? 'primary' : 'ghost'}>
|
|
||||||
{item.label}
|
|
||||||
</MarketingButton>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<div className="mt-10 grid gap-8 lg:grid-cols-[0.75fr_1.25fr]">
|
|
||||||
<div className="space-y-5">
|
|
||||||
<div className="rounded-[1.75rem] border border-[#1F3A2D]/8 bg-white p-6 shadow-[0_20px_65px_rgba(20,38,29,0.06)]">
|
|
||||||
<div className="flex items-start gap-4">
|
|
||||||
<div className="flex h-12 w-12 items-center justify-center rounded-2xl bg-[#F4F1E6] text-[#1F3A2D]">
|
|
||||||
<BaseIcon path={mdiMapMarkerOutline} />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p className="text-sm font-semibold uppercase tracking-[0.28em] text-[#9FB06F]">Location</p>
|
|
||||||
<p className="mt-3 text-sm leading-7 text-[#556257]">{brand.address}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="rounded-[1.75rem] border border-[#1F3A2D]/8 bg-white p-6 shadow-[0_20px_65px_rgba(20,38,29,0.06)]">
|
|
||||||
<div className="flex items-start gap-4">
|
|
||||||
<div className="flex h-12 w-12 items-center justify-center rounded-2xl bg-[#F4F1E6] text-[#1F3A2D]">
|
|
||||||
<BaseIcon path={mdiEmailOutline} />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p className="text-sm font-semibold uppercase tracking-[0.28em] text-[#9FB06F]">Email</p>
|
|
||||||
<p className="mt-3 text-sm leading-7 text-[#556257]">{brand.email}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="rounded-[1.75rem] border border-[#1F3A2D]/8 bg-white p-6 shadow-[0_20px_65px_rgba(20,38,29,0.06)]">
|
|
||||||
<div className="flex items-start gap-4">
|
|
||||||
<div className="flex h-12 w-12 items-center justify-center rounded-2xl bg-[#F4F1E6] text-[#1F3A2D]">
|
|
||||||
<BaseIcon path={mdiPhoneOutline} />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p className="text-sm font-semibold uppercase tracking-[0.28em] text-[#9FB06F]">Phone</p>
|
|
||||||
<div className="mt-3 space-y-1 text-sm leading-7 text-[#556257]">
|
|
||||||
{brand.phones.map((phone) => (
|
|
||||||
<p key={phone}>{phone}</p>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<InquiryForm initialInquiryType={initialType} />
|
|
||||||
</div>
|
|
||||||
</MarketingSection>
|
|
||||||
</MarketingLayout>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
ContactPage.getLayout = function getLayout(page: ReactElement) {
|
|
||||||
return <LayoutGuest>{page}</LayoutGuest>;
|
|
||||||
};
|
|
||||||
@ -1,119 +0,0 @@
|
|||||||
import {
|
|
||||||
mdiCompassOutline,
|
|
||||||
mdiLeaf,
|
|
||||||
mdiShieldCheckOutline,
|
|
||||||
mdiTruckFastOutline,
|
|
||||||
} from '@mdi/js';
|
|
||||||
import type { ReactElement } from 'react';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import BaseIcon from '../components/BaseIcon';
|
|
||||||
import MarketingButton from '../components/marketing/MarketingButton';
|
|
||||||
import MarketingLayout from '../components/marketing/MarketingLayout';
|
|
||||||
import MarketingPageHero from '../components/marketing/MarketingPageHero';
|
|
||||||
import MarketingSeo from '../components/marketing/MarketingSeo';
|
|
||||||
import MarketingSection from '../components/marketing/MarketingSection';
|
|
||||||
import { exportServiceItems } from '../components/marketing/marketingData';
|
|
||||||
import LayoutGuest from '../layouts/Guest';
|
|
||||||
|
|
||||||
const iconByHref = {
|
|
||||||
'/coffee-origins': mdiCompassOutline,
|
|
||||||
'/quality-control': mdiShieldCheckOutline,
|
|
||||||
'/sustainability': mdiLeaf,
|
|
||||||
'/logistics-export': mdiTruckFastOutline,
|
|
||||||
};
|
|
||||||
|
|
||||||
const workflowSteps = [
|
|
||||||
{
|
|
||||||
title: 'Origin matching',
|
|
||||||
description:
|
|
||||||
'We align buyer taste goals, processing preferences, and market needs with the most suitable Ethiopian origins.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Quality verification',
|
|
||||||
description:
|
|
||||||
'Lots move through disciplined selection, moisture review, and cupping evaluation before they are prepared for export.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Shipment coordination',
|
|
||||||
description:
|
|
||||||
'Documentation, communication, and logistics are handled with clarity from Addis Ababa through Djibouti to destination.',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default function ExportServicesPage() {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<MarketingSeo
|
|
||||||
title="Export Services"
|
|
||||||
description="Explore Alem Desta Coffee Export's service hub covering coffee origins, quality control, sustainability, and logistics coordination for global buyers."
|
|
||||||
/>
|
|
||||||
<MarketingLayout>
|
|
||||||
<MarketingPageHero
|
|
||||||
eyebrow="Export services"
|
|
||||||
title="A premium export hub designed for serious international coffee buyers"
|
|
||||||
description="The Export Services page brings every key decision area into one elegant overview — origin selection, quality assurance, sustainability expectations, and shipment coordination."
|
|
||||||
image="https://images.unsplash.com/photo-1520607162513-77705c0f0d4a?auto=format&fit=crop&w=1400&q=80"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<MarketingSection
|
|
||||||
eyebrow="Service hub"
|
|
||||||
title="Explore the four pillars behind our export offer"
|
|
||||||
description="Rather than overcrowding the main navigation, the website groups our detailed export capabilities here in one clear buyer-focused hub."
|
|
||||||
>
|
|
||||||
<div className="grid gap-6 md:grid-cols-2">
|
|
||||||
{exportServiceItems.map((item) => (
|
|
||||||
<article
|
|
||||||
key={item.href}
|
|
||||||
className="rounded-[2rem] border border-[#1F3A2D]/8 bg-white p-7 shadow-[0_20px_65px_rgba(20,38,29,0.06)]"
|
|
||||||
>
|
|
||||||
<div className="flex h-14 w-14 items-center justify-center rounded-2xl bg-[#F4F1E6] text-[#1F3A2D]">
|
|
||||||
<BaseIcon path={iconByHref[item.href as keyof typeof iconByHref]} />
|
|
||||||
</div>
|
|
||||||
<h2 className="font-brand-display mt-6 text-3xl text-[#1F3A2D]">{item.label}</h2>
|
|
||||||
<p className="mt-4 text-sm leading-7 text-[#556257]">{item.description}</p>
|
|
||||||
<div className="mt-6">
|
|
||||||
<MarketingButton href={item.href} variant="ghost">
|
|
||||||
View details
|
|
||||||
</MarketingButton>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</MarketingSection>
|
|
||||||
|
|
||||||
<MarketingSection
|
|
||||||
eyebrow="Buyer workflow"
|
|
||||||
title="How the export relationship is structured"
|
|
||||||
description="International buyers need more than storytelling. They need a process that feels organized, transparent, and commercially dependable."
|
|
||||||
className="bg-[#F8FAF4]"
|
|
||||||
>
|
|
||||||
<div className="grid gap-6 lg:grid-cols-3">
|
|
||||||
{workflowSteps.map((step, index) => (
|
|
||||||
<div
|
|
||||||
key={step.title}
|
|
||||||
className="rounded-[1.75rem] border border-[#1F3A2D]/8 bg-white p-7 shadow-[0_18px_55px_rgba(20,38,29,0.05)]"
|
|
||||||
>
|
|
||||||
<div className="flex h-11 w-11 items-center justify-center rounded-full bg-[#1F3A2D] text-sm font-semibold text-[#F4F1E6]">
|
|
||||||
{index + 1}
|
|
||||||
</div>
|
|
||||||
<h3 className="font-brand-display mt-6 text-2xl text-[#1F3A2D]">{step.title}</h3>
|
|
||||||
<p className="mt-4 text-sm leading-7 text-[#556257]">{step.description}</p>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<div className="mt-10 flex flex-col gap-4 sm:flex-row">
|
|
||||||
<MarketingButton href="/contact?type=request_samples">Request Samples</MarketingButton>
|
|
||||||
<MarketingButton href="/contact?type=get_a_quote" variant="ghost">
|
|
||||||
Request a tailored quote
|
|
||||||
</MarketingButton>
|
|
||||||
</div>
|
|
||||||
</MarketingSection>
|
|
||||||
</MarketingLayout>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
ExportServicesPage.getLayout = function getLayout(page: ReactElement) {
|
|
||||||
return <LayoutGuest>{page}</LayoutGuest>;
|
|
||||||
};
|
|
||||||
@ -1,258 +1,166 @@
|
|||||||
import {
|
|
||||||
mdiEarth,
|
import React, { useEffect, useState } from 'react';
|
||||||
mdiLeaf,
|
|
||||||
mdiMapMarker,
|
|
||||||
mdiShieldCheck,
|
|
||||||
mdiTruckFastOutline,
|
|
||||||
} from '@mdi/js';
|
|
||||||
import type { ReactElement } from 'react';
|
import type { ReactElement } from 'react';
|
||||||
import React from 'react';
|
import Head from 'next/head';
|
||||||
|
import Link from 'next/link';
|
||||||
import BaseIcon from '../components/BaseIcon';
|
import BaseButton from '../components/BaseButton';
|
||||||
import MarketingButton from '../components/marketing/MarketingButton';
|
import CardBox from '../components/CardBox';
|
||||||
import MarketingLayout from '../components/marketing/MarketingLayout';
|
import SectionFullScreen from '../components/SectionFullScreen';
|
||||||
import MarketingSection from '../components/marketing/MarketingSection';
|
|
||||||
import MarketingSeo from '../components/marketing/MarketingSeo';
|
|
||||||
import NewsletterForm from '../components/marketing/NewsletterForm';
|
|
||||||
import {
|
|
||||||
brand,
|
|
||||||
exportServices,
|
|
||||||
featureHighlights,
|
|
||||||
galleryItems,
|
|
||||||
origins,
|
|
||||||
testimonials,
|
|
||||||
} from '../components/marketing/marketingData';
|
|
||||||
import LayoutGuest from '../layouts/Guest';
|
import LayoutGuest from '../layouts/Guest';
|
||||||
|
import BaseDivider from '../components/BaseDivider';
|
||||||
|
import BaseButtons from '../components/BaseButtons';
|
||||||
|
import { getPageTitle } from '../config';
|
||||||
|
import { useAppSelector } from '../stores/hooks';
|
||||||
|
import CardBoxComponentTitle from "../components/CardBoxComponentTitle";
|
||||||
|
import { getPexelsImage, getPexelsVideo } from '../helpers/pexels';
|
||||||
|
|
||||||
const statItems = [
|
|
||||||
{ label: 'Origin-driven sourcing', value: 'Ethiopia first' },
|
|
||||||
{ label: 'Export route', value: 'Addis Ababa → Djibouti → Global markets' },
|
|
||||||
{ label: 'Buyer focus', value: 'Roasters, importers, distributors, wholesalers' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const trustPillars = [
|
export default function Starter() {
|
||||||
{
|
const [illustrationImage, setIllustrationImage] = useState({
|
||||||
icon: mdiShieldCheck,
|
src: undefined,
|
||||||
title: 'Trust & transparency',
|
photographer: undefined,
|
||||||
description: 'Clear communication, honest documentation, and disciplined follow-through for international buyers.',
|
photographer_url: undefined,
|
||||||
},
|
})
|
||||||
{
|
const [illustrationVideo, setIllustrationVideo] = useState({video_files: []})
|
||||||
icon: mdiMapMarker,
|
const [contentType, setContentType] = useState('video');
|
||||||
title: 'Origin expertise',
|
const [contentPosition, setContentPosition] = useState('right');
|
||||||
description: 'Regional understanding supports lot selection aligned with buyer taste profiles and sourcing goals.',
|
const textColor = useAppSelector((state) => state.style.linkColor);
|
||||||
},
|
|
||||||
{
|
const title = 'Alem Desta Coffee Export Site'
|
||||||
icon: mdiTruckFastOutline,
|
|
||||||
title: 'Export reliability',
|
// Fetch Pexels image/video
|
||||||
description: 'A structured logistics chain supports timely movement from origin to destination.',
|
useEffect(() => {
|
||||||
},
|
async function fetchData() {
|
||||||
{
|
const image = await getPexelsImage();
|
||||||
icon: mdiLeaf,
|
const video = await getPexelsVideo();
|
||||||
title: 'Responsible sourcing',
|
setIllustrationImage(image);
|
||||||
description: 'Sustainability is treated as a business responsibility, not a campaign message.',
|
setIllustrationVideo(video);
|
||||||
},
|
}
|
||||||
];
|
fetchData();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const imageBlock = (image) => (
|
||||||
|
<div
|
||||||
|
className='hidden md:flex flex-col justify-end relative flex-grow-0 flex-shrink-0 w-1/3'
|
||||||
|
style={{
|
||||||
|
backgroundImage: `${
|
||||||
|
image
|
||||||
|
? `url(${image?.src?.original})`
|
||||||
|
: 'linear-gradient(rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.5))'
|
||||||
|
}`,
|
||||||
|
backgroundSize: 'cover',
|
||||||
|
backgroundPosition: 'left center',
|
||||||
|
backgroundRepeat: 'no-repeat',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className='flex justify-center w-full bg-blue-300/20'>
|
||||||
|
<a
|
||||||
|
className='text-[8px]'
|
||||||
|
href={image?.photographer_url}
|
||||||
|
target='_blank'
|
||||||
|
rel='noreferrer'
|
||||||
|
>
|
||||||
|
Photo by {image?.photographer} on Pexels
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const videoBlock = (video) => {
|
||||||
|
if (video?.video_files?.length > 0) {
|
||||||
|
return (
|
||||||
|
<div className='hidden md:flex flex-col justify-end relative flex-grow-0 flex-shrink-0 w-1/3'>
|
||||||
|
<video
|
||||||
|
className='absolute top-0 left-0 w-full h-full object-cover'
|
||||||
|
autoPlay
|
||||||
|
loop
|
||||||
|
muted
|
||||||
|
>
|
||||||
|
<source src={video?.video_files[0]?.link} type='video/mp4'/>
|
||||||
|
Your browser does not support the video tag.
|
||||||
|
</video>
|
||||||
|
<div className='flex justify-center w-full bg-blue-300/20 z-10'>
|
||||||
|
<a
|
||||||
|
className='text-[8px]'
|
||||||
|
href={video?.user?.url}
|
||||||
|
target='_blank'
|
||||||
|
rel='noreferrer'
|
||||||
|
>
|
||||||
|
Video by {video.user.name} on Pexels
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export default function HomePage() {
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div
|
||||||
<MarketingSeo
|
style={
|
||||||
title="Ethiopian Premium Coffee Export"
|
contentPosition === 'background'
|
||||||
description="Premium Ethiopian coffee exporter connecting origin, traceability, and global buyer confidence with elegant, professional service."
|
? {
|
||||||
/>
|
backgroundImage: `${
|
||||||
<MarketingLayout>
|
illustrationImage
|
||||||
<section className="relative -mt-[88px] overflow-hidden bg-[#16271F] px-5 pb-16 pt-36 text-white lg:px-8 lg:pb-24 lg:pt-44">
|
? `url(${illustrationImage.src?.original})`
|
||||||
<div className="absolute inset-0">
|
: 'linear-gradient(rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.5))'
|
||||||
<img
|
}`,
|
||||||
className="h-full w-full scale-[1.02] object-cover opacity-32 saturate-[0.82]"
|
backgroundSize: 'cover',
|
||||||
loading="eager"
|
backgroundPosition: 'left center',
|
||||||
src="https://images.unsplash.com/photo-1447933601403-0c6688de566e?auto=format&fit=crop&w=1600&q=80"
|
backgroundRepeat: 'no-repeat',
|
||||||
alt="Ethiopian coffee farm"
|
}
|
||||||
/>
|
: {}
|
||||||
<div className="absolute inset-0 bg-[linear-gradient(135deg,rgba(18,38,29,0.88),rgba(31,58,45,0.72),rgba(18,38,29,0.9))]" />
|
}
|
||||||
</div>
|
>
|
||||||
|
<Head>
|
||||||
|
<title>{getPageTitle('Starter Page')}</title>
|
||||||
|
</Head>
|
||||||
|
|
||||||
<div className="coffee-fade-in relative mx-auto grid max-w-7xl gap-10 lg:grid-cols-[1.05fr_0.95fr] lg:items-end">
|
<SectionFullScreen bg='violet'>
|
||||||
<div>
|
<div
|
||||||
<p className="text-sm font-semibold uppercase tracking-[0.38em] text-[#B7C78B]">Specialty green coffee export</p>
|
className={`flex ${
|
||||||
<h1 className="font-brand-display mt-6 max-w-4xl text-5xl leading-[1.05] text-[#F4F1E6] md:text-7xl">
|
contentPosition === 'right' ? 'flex-row-reverse' : 'flex-row'
|
||||||
Ethiopian Premium Coffee – Sourced with Experience, Exported with Integrity
|
} min-h-screen w-full`}
|
||||||
</h1>
|
>
|
||||||
<p className="mt-6 max-w-3xl text-lg leading-8 text-white/78">
|
{contentType === 'image' && contentPosition !== 'background'
|
||||||
Achieving premium coffee excellence from its origin.
|
? imageBlock(illustrationImage)
|
||||||
</p>
|
: null}
|
||||||
<p className="mt-6 max-w-3xl text-base leading-8 text-white/72">
|
{contentType === 'video' && contentPosition !== 'background'
|
||||||
{brand.name} bridges Ethiopian premium coffee producers with global buyers, focusing on traceability,
|
? videoBlock(illustrationVideo)
|
||||||
single-origin green coffee sourcing, export-ready preparation, and uncompromising quality.
|
: null}
|
||||||
</p>
|
<div className='flex items-center justify-center flex-col space-y-4 w-full lg:w-full'>
|
||||||
<div className="mt-10 flex flex-col gap-4 sm:flex-row">
|
<CardBox className='w-full md:w-3/5 lg:w-2/3'>
|
||||||
<MarketingButton href="/contact?type=request_samples">Request Samples</MarketingButton>
|
<CardBoxComponentTitle title="Welcome to your Alem Desta Coffee Export Site app!"/>
|
||||||
<MarketingButton href="/contact?type=get_a_quote" variant="secondary">
|
|
||||||
Get a Quote
|
<div className="space-y-3">
|
||||||
</MarketingButton>
|
<p className='text-center text-gray-500'>This is a React.js/Node.js app generated by the <a className={`${textColor}`} href="https://flatlogic.com/generator">Flatlogic Web App Generator</a></p>
|
||||||
</div>
|
<p className='text-center text-gray-500'>For guides and documentation please check
|
||||||
|
your local README.md and the <a className={`${textColor}`} href="https://flatlogic.com/documentation">Flatlogic documentation</a></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="rounded-[2rem] border border-white/10 bg-white/10 p-6 backdrop-blur-md shadow-[0_30px_80px_rgba(0,0,0,0.25)] md:p-8">
|
<BaseButtons>
|
||||||
<p className="text-sm font-semibold uppercase tracking-[0.28em] text-[#B7C78B]">Why buyers trust us</p>
|
<BaseButton
|
||||||
<div className="mt-8 space-y-5">
|
href='/login'
|
||||||
{statItems.map((item) => (
|
label='Login'
|
||||||
<div key={item.label} className="border-b border-white/10 pb-5 last:border-b-0 last:pb-0">
|
color='info'
|
||||||
<p className="text-sm uppercase tracking-[0.28em] text-white/48">{item.label}</p>
|
className='w-full'
|
||||||
<p className="mt-2 text-lg font-medium text-white">{item.value}</p>
|
/>
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<MarketingSection
|
</BaseButtons>
|
||||||
eyebrow="Immediate confidence"
|
</CardBox>
|
||||||
title="A refined sourcing experience for global coffee buyers"
|
</div>
|
||||||
description="The first impression international buyers need is clarity: where coffee comes from, how quality is protected, and whether export communication will be dependable. This website presents that trust story with elegance and precision."
|
</div>
|
||||||
>
|
</SectionFullScreen>
|
||||||
<div className="grid gap-6 md:grid-cols-2 xl:grid-cols-4">
|
<div className='bg-black text-white flex flex-col text-center justify-center md:flex-row'>
|
||||||
{trustPillars.map((item) => (
|
<p className='py-6 text-sm'>© 2026 <span>{title}</span>. All rights reserved</p>
|
||||||
<div key={item.title} className="rounded-[1.75rem] border border-[#1F3A2D]/8 bg-white p-6 shadow-[0_20px_65px_rgba(20,38,29,0.06)]">
|
<Link className='py-6 ml-4 text-sm' href='/privacy-policy/'>
|
||||||
<div className="flex h-12 w-12 items-center justify-center rounded-2xl bg-[#F4F1E6] text-[#1F3A2D]">
|
Privacy Policy
|
||||||
<BaseIcon path={item.icon} />
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<h3 className="font-brand-display mt-5 text-2xl text-[#1F3A2D]">{item.title}</h3>
|
|
||||||
<p className="mt-3 text-sm leading-7 text-[#556257]">{item.description}</p>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</MarketingSection>
|
|
||||||
|
|
||||||
<MarketingSection
|
</div>
|
||||||
eyebrow="Feature highlights"
|
|
||||||
title="Built around traceability, premium handling, and long-term export partnerships"
|
|
||||||
description="Each highlight below mirrors the core buying questions of roasters, green coffee importers, and wholesale partners evaluating a supplier relationship."
|
|
||||||
centered
|
|
||||||
className="bg-[#F8FAF4]"
|
|
||||||
>
|
|
||||||
<div className="grid gap-6 lg:grid-cols-2 xl:grid-cols-4">
|
|
||||||
{featureHighlights.map((item) => (
|
|
||||||
<div key={item.title} className="rounded-[1.75rem] border border-[#1F3A2D]/8 bg-white p-7 shadow-[0_20px_70px_rgba(20,38,29,0.07)]">
|
|
||||||
<h3 className="font-brand-display text-2xl text-[#1F3A2D]">{item.title}</h3>
|
|
||||||
<p className="mt-4 text-sm leading-7 text-[#556257]">{item.description}</p>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</MarketingSection>
|
|
||||||
|
|
||||||
<MarketingSection
|
|
||||||
eyebrow="Visual origin story"
|
|
||||||
title="From farms to cupping tables to export readiness"
|
|
||||||
description="The buyer journey starts with visual confidence: clean origin imagery, disciplined processing, and a clear sense of how coffee moves professionally toward shipment."
|
|
||||||
>
|
|
||||||
<div className="grid gap-6 md:grid-cols-2">
|
|
||||||
{galleryItems.map((item) => (
|
|
||||||
<article key={item.title} className="group overflow-hidden rounded-[2rem] bg-white shadow-[0_24px_70px_rgba(20,38,29,0.08)]">
|
|
||||||
<div className="relative h-72 overflow-hidden">
|
|
||||||
<img
|
|
||||||
className="h-full w-full object-cover saturate-[0.88] transition duration-700 group-hover:scale-105"
|
|
||||||
loading="lazy"
|
|
||||||
src={item.image}
|
|
||||||
alt={item.title}
|
|
||||||
/>
|
|
||||||
<div className="absolute inset-0 bg-[linear-gradient(180deg,rgba(18,38,29,0.08),rgba(18,38,29,0),rgba(18,38,29,0.28))]" />
|
|
||||||
</div>
|
|
||||||
<div className="p-6">
|
|
||||||
<h3 className="font-brand-display text-2xl text-[#1F3A2D]">{item.title}</h3>
|
|
||||||
<p className="mt-3 text-sm leading-7 text-[#556257]">{item.caption}</p>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</MarketingSection>
|
|
||||||
|
|
||||||
<MarketingSection
|
|
||||||
eyebrow="Origin snapshots"
|
|
||||||
title="Distinctive Ethiopian regions, each with its own flavor narrative"
|
|
||||||
description="A quick regional preview gives buyers a sense of sourcing depth before they request samples or a tailored quote."
|
|
||||||
className="bg-[#F4F1E6]/45"
|
|
||||||
>
|
|
||||||
<div className="grid gap-6 lg:grid-cols-3">
|
|
||||||
{origins.slice(0, 3).map((item) => (
|
|
||||||
<div key={item.name} className="overflow-hidden rounded-[2rem] bg-white shadow-[0_24px_70px_rgba(20,38,29,0.08)]">
|
|
||||||
<div className="relative">
|
|
||||||
<img className="h-56 w-full object-cover saturate-[0.9]" loading="lazy" src={item.image} alt={item.name} />
|
|
||||||
<div className="absolute inset-0 bg-[linear-gradient(180deg,rgba(18,38,29,0.05),rgba(18,38,29,0),rgba(18,38,29,0.24))]" />
|
|
||||||
</div>
|
|
||||||
<div className="p-6">
|
|
||||||
<p className="text-sm uppercase tracking-[0.28em] text-[#9FB06F]">{item.altitude}</p>
|
|
||||||
<h3 className="font-brand-display mt-3 text-2xl text-[#1F3A2D]">{item.name}</h3>
|
|
||||||
<p className="mt-3 text-sm leading-7 text-[#556257]">{item.profile}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<div className="mt-8">
|
|
||||||
<MarketingButton href="/coffee-origins" variant="ghost">
|
|
||||||
Explore all origins
|
|
||||||
</MarketingButton>
|
|
||||||
</div>
|
|
||||||
</MarketingSection>
|
|
||||||
|
|
||||||
<MarketingSection
|
|
||||||
eyebrow="Export capability"
|
|
||||||
title="Professional export services built for international buyers"
|
|
||||||
description="Reliable logistics, internal documentation handling, and strong communication help create a premium corporate buying experience."
|
|
||||||
>
|
|
||||||
<div className="grid gap-4 md:grid-cols-2">
|
|
||||||
{exportServices.slice(0, 4).map((item) => (
|
|
||||||
<div key={item} className="flex gap-4 rounded-[1.5rem] border border-[#1F3A2D]/8 bg-white p-5 shadow-[0_18px_60px_rgba(20,38,29,0.06)]">
|
|
||||||
<div className="mt-1 text-[#9FB06F]">
|
|
||||||
<BaseIcon path={mdiEarth} />
|
|
||||||
</div>
|
|
||||||
<p className="text-sm leading-7 text-[#556257]">{item}</p>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</MarketingSection>
|
|
||||||
|
|
||||||
<MarketingSection
|
|
||||||
eyebrow="Testimonials"
|
|
||||||
title="Commercial credibility, with room to grow into buyer proof"
|
|
||||||
description="These premium placeholders establish the intended testimonial zone so future buyer feedback can be added without redesigning the site."
|
|
||||||
centered
|
|
||||||
className="bg-[#F8FAF4]"
|
|
||||||
>
|
|
||||||
<div className="grid gap-6 lg:grid-cols-2">
|
|
||||||
{testimonials.map((item) => (
|
|
||||||
<blockquote key={item.author} className="rounded-[1.75rem] border border-[#1F3A2D]/8 bg-white p-7 shadow-[0_18px_60px_rgba(20,38,29,0.06)]">
|
|
||||||
<p className="font-brand-display text-2xl leading-10 text-[#1F3A2D]">“{item.quote}”</p>
|
|
||||||
<footer className="mt-6 text-sm font-medium uppercase tracking-[0.24em] text-[#72816F]">{item.author}</footer>
|
|
||||||
</blockquote>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</MarketingSection>
|
|
||||||
|
|
||||||
<MarketingSection
|
|
||||||
eyebrow="Stay informed"
|
|
||||||
title="Subscribe for sourcing updates"
|
|
||||||
description="Newsletter sign-up is already connected to the admin subscriber list, giving your team a real conversion path from day one."
|
|
||||||
className="bg-[#1F3A2D] text-white"
|
|
||||||
>
|
|
||||||
<div className="grid gap-10 lg:grid-cols-[1fr_0.9fr] lg:items-center">
|
|
||||||
<div>
|
|
||||||
<h3 className="font-brand-display text-4xl text-[#F4F1E6]">Receive buyer-focused green coffee updates</h3>
|
|
||||||
<p className="mt-5 max-w-2xl text-base leading-8 text-white/72">
|
|
||||||
Join the list for availability updates, new origin highlights, and company announcements shaped for professional buyers.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="rounded-[2rem] border border-white/10 bg-white/10 p-6 backdrop-blur-md">
|
|
||||||
<NewsletterForm source="home_cta" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</MarketingSection>
|
|
||||||
</MarketingLayout>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
HomePage.getLayout = function getLayout(page: ReactElement) {
|
Starter.getLayout = function getLayout(page: ReactElement) {
|
||||||
return <LayoutGuest>{page}</LayoutGuest>;
|
return <LayoutGuest>{page}</LayoutGuest>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,109 +0,0 @@
|
|||||||
import { mdiFileDocumentOutline, mdiMessageTextFastOutline, mdiPackageVariantClosed, mdiTruckFastOutline } from '@mdi/js';
|
|
||||||
import type { ReactElement } from 'react';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import BaseIcon from '../components/BaseIcon';
|
|
||||||
import MarketingLayout from '../components/marketing/MarketingLayout';
|
|
||||||
import MarketingPageHero from '../components/marketing/MarketingPageHero';
|
|
||||||
import MarketingSeo from '../components/marketing/MarketingSeo';
|
|
||||||
import MarketingSection from '../components/marketing/MarketingSection';
|
|
||||||
import LayoutGuest from '../layouts/Guest';
|
|
||||||
|
|
||||||
const logisticsPillars = [
|
|
||||||
{
|
|
||||||
icon: mdiFileDocumentOutline,
|
|
||||||
title: 'Documentation handling',
|
|
||||||
description:
|
|
||||||
'Export paperwork is managed with care to support smoother communication and reduce uncertainty for international buyers.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: mdiPackageVariantClosed,
|
|
||||||
title: 'Shipment preparation',
|
|
||||||
description:
|
|
||||||
'Lots are prepared for export with disciplined packaging, timing coordination, and readiness checks before dispatch.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: mdiTruckFastOutline,
|
|
||||||
title: 'Route coordination',
|
|
||||||
description:
|
|
||||||
'The logistics chain is organized from Addis Ababa to Djibouti and onward to destination markets with responsive updates.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: mdiMessageTextFastOutline,
|
|
||||||
title: 'Buyer communication',
|
|
||||||
description:
|
|
||||||
'Prompt communication keeps importers, roasters, and distributors informed throughout the export workflow.',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const timeline = [
|
|
||||||
'Lot confirmation and shipment planning',
|
|
||||||
'Internal document preparation and export coordination',
|
|
||||||
'Movement from origin handling points toward Djibouti',
|
|
||||||
'Final export dispatch with clear buyer communication',
|
|
||||||
];
|
|
||||||
|
|
||||||
export default function LogisticsExportPage() {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<MarketingSeo
|
|
||||||
title="Logistics & Export"
|
|
||||||
description="Learn how Alem Desta Coffee Export coordinates documentation, shipment preparation, and export communication from Ethiopia to global markets."
|
|
||||||
/>
|
|
||||||
<MarketingLayout>
|
|
||||||
<MarketingPageHero
|
|
||||||
eyebrow="Logistics & export"
|
|
||||||
title="Export coordination that feels disciplined, responsive, and globally ready"
|
|
||||||
description="For premium buyers, logistics is part of trust. We present the export chain with the same clarity and professionalism expected from the coffee itself."
|
|
||||||
image="https://images.unsplash.com/photo-1520607162513-77705c0f0d4a?auto=format&fit=crop&w=1400&q=80"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<MarketingSection
|
|
||||||
eyebrow="Operational confidence"
|
|
||||||
title="The logistics side of the business is built to reduce friction"
|
|
||||||
description="This page gives buyers a concise view of how coffee moves from Ethiopian origin into an organized export workflow."
|
|
||||||
>
|
|
||||||
<div className="grid gap-6 md:grid-cols-2 xl:grid-cols-4">
|
|
||||||
{logisticsPillars.map((item) => (
|
|
||||||
<div
|
|
||||||
key={item.title}
|
|
||||||
className="rounded-[1.75rem] border border-[#1F3A2D]/8 bg-white p-6 shadow-[0_20px_65px_rgba(20,38,29,0.06)]"
|
|
||||||
>
|
|
||||||
<div className="flex h-12 w-12 items-center justify-center rounded-2xl bg-[#F4F1E6] text-[#1F3A2D]">
|
|
||||||
<BaseIcon path={item.icon} />
|
|
||||||
</div>
|
|
||||||
<h2 className="font-brand-display mt-5 text-2xl text-[#1F3A2D]">{item.title}</h2>
|
|
||||||
<p className="mt-4 text-sm leading-7 text-[#556257]">{item.description}</p>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</MarketingSection>
|
|
||||||
|
|
||||||
<MarketingSection
|
|
||||||
eyebrow="Export flow"
|
|
||||||
title="A simple view of how the shipment process is organized"
|
|
||||||
description="The sequence below reinforces a premium message: disciplined coffee handling, timely coordination, and dependable communication."
|
|
||||||
className="bg-[#F8FAF4]"
|
|
||||||
>
|
|
||||||
<div className="grid gap-4 lg:grid-cols-2">
|
|
||||||
{timeline.map((step, index) => (
|
|
||||||
<div
|
|
||||||
key={step}
|
|
||||||
className="flex items-center gap-4 rounded-[1.5rem] border border-[#1F3A2D]/8 bg-white px-5 py-4 shadow-[0_18px_55px_rgba(20,38,29,0.05)]"
|
|
||||||
>
|
|
||||||
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-[#1F3A2D] text-sm font-semibold text-[#F4F1E6]">
|
|
||||||
{index + 1}
|
|
||||||
</div>
|
|
||||||
<p className="text-sm leading-7 text-[#556257]">{step}</p>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</MarketingSection>
|
|
||||||
</MarketingLayout>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
LogisticsExportPage.getLayout = function getLayout(page: ReactElement) {
|
|
||||||
return <LayoutGuest>{page}</LayoutGuest>;
|
|
||||||
};
|
|
||||||
@ -1,79 +0,0 @@
|
|||||||
import { mdiChartWaterfall, mdiCupOutline, mdiWaterOutline } from '@mdi/js';
|
|
||||||
import type { ReactElement } from 'react';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import BaseIcon from '../components/BaseIcon';
|
|
||||||
import MarketingLayout from '../components/marketing/MarketingLayout';
|
|
||||||
import MarketingPageHero from '../components/marketing/MarketingPageHero';
|
|
||||||
import MarketingSeo from '../components/marketing/MarketingSeo';
|
|
||||||
import MarketingSection from '../components/marketing/MarketingSection';
|
|
||||||
import { qualityMetrics, qualityProcesses } from '../components/marketing/marketingData';
|
|
||||||
import LayoutGuest from '../layouts/Guest';
|
|
||||||
|
|
||||||
const processIcons = [mdiWaterOutline, mdiChartWaterfall, mdiCupOutline];
|
|
||||||
|
|
||||||
export default function QualityControlPage() {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<MarketingSeo
|
|
||||||
title="Quality Control"
|
|
||||||
description="Washed, natural, and honey processing workflows with moisture control and cupping evaluation standards."
|
|
||||||
/>
|
|
||||||
<MarketingLayout>
|
|
||||||
<MarketingPageHero
|
|
||||||
eyebrow="Quality control"
|
|
||||||
title="Structured processing and disciplined quality review"
|
|
||||||
description="This page turns processing detail into buyer confidence by showing how washed, natural, and honey methods are handled and evaluated."
|
|
||||||
image="https://images.unsplash.com/photo-1511920170033-f8396924c348?auto=format&fit=crop&w=1400&q=80"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<MarketingSection
|
|
||||||
eyebrow="Processing systems"
|
|
||||||
title="Three process pathways, each explained with clarity"
|
|
||||||
description="The goal is simple: make process detail easy to read, visually elegant, and commercially reassuring."
|
|
||||||
>
|
|
||||||
<div className="grid gap-6 lg:grid-cols-3">
|
|
||||||
{qualityProcesses.map((item, index) => (
|
|
||||||
<div key={item.title} className="rounded-[1.85rem] border border-[#1F3A2D]/8 bg-white p-7 shadow-[0_24px_70px_rgba(20,38,29,0.06)]">
|
|
||||||
<div className="flex h-12 w-12 items-center justify-center rounded-2xl bg-[#F4F1E6] text-[#1F3A2D]">
|
|
||||||
<BaseIcon path={processIcons[index]} />
|
|
||||||
</div>
|
|
||||||
<h3 className="font-brand-display mt-5 text-3xl text-[#1F3A2D]">{item.title}</h3>
|
|
||||||
<p className="mt-4 text-sm leading-7 text-[#556257]">{item.highlight}</p>
|
|
||||||
<div className="mt-6 space-y-3">
|
|
||||||
{item.steps.map((step, stepIndex) => (
|
|
||||||
<div key={step} className="flex items-center gap-3 rounded-2xl bg-[#EEF3E7] px-4 py-3 text-sm text-[#4F5E53]">
|
|
||||||
<span className="flex h-8 w-8 items-center justify-center rounded-full bg-[#1F3A2D] text-xs font-semibold text-[#F4F1E6]">
|
|
||||||
{stepIndex + 1}
|
|
||||||
</span>
|
|
||||||
{step}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</MarketingSection>
|
|
||||||
|
|
||||||
<MarketingSection
|
|
||||||
eyebrow="Quality system"
|
|
||||||
title="Measured, tasted, and reviewed before export"
|
|
||||||
description="Moisture control and cupping evaluation help translate process discipline into export confidence."
|
|
||||||
className="bg-[#F8FAF4]"
|
|
||||||
>
|
|
||||||
<div className="grid gap-4 md:grid-cols-2 xl:grid-cols-3">
|
|
||||||
{qualityMetrics.map((item) => (
|
|
||||||
<div key={item} className="rounded-[1.5rem] border border-[#1F3A2D]/8 bg-white px-5 py-4 text-sm leading-7 text-[#556257] shadow-[0_18px_55px_rgba(20,38,29,0.05)]">
|
|
||||||
{item}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</MarketingSection>
|
|
||||||
</MarketingLayout>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
QualityControlPage.getLayout = function getLayout(page: ReactElement) {
|
|
||||||
return <LayoutGuest>{page}</LayoutGuest>;
|
|
||||||
};
|
|
||||||
@ -1,47 +0,0 @@
|
|||||||
import type { ReactElement } from 'react';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import MarketingLayout from '../components/marketing/MarketingLayout';
|
|
||||||
import MarketingPageHero from '../components/marketing/MarketingPageHero';
|
|
||||||
import MarketingSeo from '../components/marketing/MarketingSeo';
|
|
||||||
import MarketingSection from '../components/marketing/MarketingSection';
|
|
||||||
import { sustainabilityPoints } from '../components/marketing/marketingData';
|
|
||||||
import LayoutGuest from '../layouts/Guest';
|
|
||||||
|
|
||||||
export default function SustainabilityPage() {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<MarketingSeo
|
|
||||||
title="Sustainability"
|
|
||||||
description="Fair payment, ethical sourcing, environmental care, and community development framed as a real business responsibility."
|
|
||||||
/>
|
|
||||||
<MarketingLayout>
|
|
||||||
<MarketingPageHero
|
|
||||||
eyebrow="Sustainability"
|
|
||||||
title="Sustainability is our responsibility, not a marketing claim"
|
|
||||||
description="This message anchors the brand in seriousness and credibility, especially for buyers evaluating long-term sourcing relationships."
|
|
||||||
image="https://images.unsplash.com/photo-1461988320302-91bde64fc8e4?auto=format&fit=crop&w=1400&q=80"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<MarketingSection
|
|
||||||
eyebrow="Responsible sourcing"
|
|
||||||
title="A business approach that respects people, land, and long-term supply"
|
|
||||||
description="Sustainability should be visible in sourcing behavior, not only in headlines."
|
|
||||||
centered
|
|
||||||
>
|
|
||||||
<div className="grid gap-6 md:grid-cols-2">
|
|
||||||
{sustainabilityPoints.map((item) => (
|
|
||||||
<div key={item} className="rounded-[1.75rem] border border-[#1F3A2D]/8 bg-white p-7 text-sm leading-7 text-[#556257] shadow-[0_20px_65px_rgba(20,38,29,0.06)]">
|
|
||||||
{item}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</MarketingSection>
|
|
||||||
</MarketingLayout>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
SustainabilityPage.getLayout = function getLayout(page: ReactElement) {
|
|
||||||
return <LayoutGuest>{page}</LayoutGuest>;
|
|
||||||
};
|
|
||||||
@ -1,72 +0,0 @@
|
|||||||
import { mdiHandshakeOutline, mdiLeaf, mdiMedalOutline, mdiScaleBalance, mdiSeedOutline, mdiShieldCheck } from '@mdi/js';
|
|
||||||
import type { ReactElement } from 'react';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import BaseIcon from '../components/BaseIcon';
|
|
||||||
import MarketingLayout from '../components/marketing/MarketingLayout';
|
|
||||||
import MarketingPageHero from '../components/marketing/MarketingPageHero';
|
|
||||||
import MarketingSeo from '../components/marketing/MarketingSeo';
|
|
||||||
import MarketingSection from '../components/marketing/MarketingSection';
|
|
||||||
import { principles, values } from '../components/marketing/marketingData';
|
|
||||||
import LayoutGuest from '../layouts/Guest';
|
|
||||||
|
|
||||||
const icons = [mdiShieldCheck, mdiSeedOutline, mdiMedalOutline, mdiHandshakeOutline, mdiScaleBalance, mdiLeaf];
|
|
||||||
|
|
||||||
export default function VisionMissionValuesPage() {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<MarketingSeo
|
|
||||||
title="Vision Mission and Values"
|
|
||||||
description="Vision, mission, values, and business principles that define Alem Desta Coffee Export."
|
|
||||||
/>
|
|
||||||
<MarketingLayout>
|
|
||||||
<MarketingPageHero
|
|
||||||
eyebrow="Vision, mission & values"
|
|
||||||
title="A long-term export business built on trust, quality, and partnership"
|
|
||||||
description="International buyers need more than attractive language. They need a clear statement of vision, mission, principles, and values that signal operational maturity."
|
|
||||||
image="https://images.unsplash.com/photo-1442512595331-e89e73853f31?auto=format&fit=crop&w=1400&q=80"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<MarketingSection title="Vision" description="To become a globally trusted Ethiopian coffee exporter known for quality, traceability, and long-term partnerships." />
|
|
||||||
<MarketingSection title="Mission" description="To export premium Ethiopian coffee with strict quality control, transparency, and sustainable partnerships." className="pt-0" />
|
|
||||||
|
|
||||||
<MarketingSection
|
|
||||||
eyebrow="Core values"
|
|
||||||
title="The values that guide how we source, communicate, and deliver"
|
|
||||||
centered
|
|
||||||
className="bg-[#F8FAF4]"
|
|
||||||
>
|
|
||||||
<div className="grid gap-6 md:grid-cols-2 xl:grid-cols-3">
|
|
||||||
{values.map((item, index) => (
|
|
||||||
<div key={item.title} className="rounded-[1.75rem] border border-[#1F3A2D]/8 bg-white p-7 shadow-[0_20px_65px_rgba(20,38,29,0.06)]">
|
|
||||||
<div className="flex h-12 w-12 items-center justify-center rounded-2xl bg-[#F4F1E6] text-[#1F3A2D]">
|
|
||||||
<BaseIcon path={icons[index]} />
|
|
||||||
</div>
|
|
||||||
<h3 className="font-brand-display mt-5 text-2xl text-[#1F3A2D]">{item.title}</h3>
|
|
||||||
<p className="mt-4 text-sm leading-7 text-[#556257]">{item.description}</p>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</MarketingSection>
|
|
||||||
|
|
||||||
<MarketingSection
|
|
||||||
eyebrow="Principles"
|
|
||||||
title="Execution principles buyers can recognize immediately"
|
|
||||||
description="These principles turn brand values into supplier behavior."
|
|
||||||
>
|
|
||||||
<div className="grid gap-4 md:grid-cols-2">
|
|
||||||
{principles.map((item) => (
|
|
||||||
<div key={item} className="rounded-[1.5rem] border border-[#1F3A2D]/8 bg-white px-5 py-4 text-sm font-medium text-[#1F3A2D] shadow-[0_18px_60px_rgba(20,38,29,0.05)]">
|
|
||||||
{item}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</MarketingSection>
|
|
||||||
</MarketingLayout>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
VisionMissionValuesPage.getLayout = function getLayout(page: ReactElement) {
|
|
||||||
return <LayoutGuest>{page}</LayoutGuest>;
|
|
||||||
};
|
|
||||||
Loading…
x
Reference in New Issue
Block a user