chore: overhaul infrastructure and integrate @mintel packages
Some checks failed
🧪 CI (QA) / 🧪 Quality Assurance (push) Failing after 1m3s
Some checks failed
🧪 CI (QA) / 🧪 Quality Assurance (push) Failing after 1m3s
- Restructure to pnpm monorepo (site moved to apps/web) - Integrate @mintel/tsconfig, @mintel/eslint-config, @mintel/husky-config - Implement Docker service architecture (Varnish, Directus, Gatekeeper) - Setup environment-aware Gitea Actions deployment
This commit is contained in:
150
apps/web/src/components/ContactForm/steps/PresenceStep.tsx
Normal file
150
apps/web/src/components/ContactForm/steps/PresenceStep.tsx
Normal file
@@ -0,0 +1,150 @@
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
import { FormState } from '../types';
|
||||
import { Checkbox } from '../components/Checkbox';
|
||||
import { Link2, Globe, Share2, Instagram, Linkedin, Facebook, Twitter, Youtube } from 'lucide-react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { Reveal } from '../../Reveal';
|
||||
import { Input } from '../components/Input';
|
||||
|
||||
interface PresenceStepProps {
|
||||
state: FormState;
|
||||
updateState: (updates: Partial<FormState>) => void;
|
||||
toggleItem: (list: string[], id: string) => string[];
|
||||
}
|
||||
|
||||
export function PresenceStep({ state, updateState, toggleItem }: PresenceStepProps) {
|
||||
const updateUrl = (id: string, url: string) => {
|
||||
updateState({
|
||||
socialMediaUrls: {
|
||||
...state.socialMediaUrls,
|
||||
[id]: url
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const SOCIAL_PLATFORMS = [
|
||||
{ id: 'instagram', label: 'Instagram', icon: Instagram },
|
||||
{ id: 'linkedin', label: 'LinkedIn', icon: Linkedin },
|
||||
{ id: 'facebook', label: 'Facebook', icon: Facebook },
|
||||
{ id: 'twitter', label: 'Twitter / X', icon: Twitter },
|
||||
{ id: 'youtube', label: 'YouTube', icon: Youtube },
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="space-y-16">
|
||||
<Reveal width="100%" delay={0.1}>
|
||||
<div className="space-y-8">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-12 h-12 bg-slate-50 rounded-2xl flex items-center justify-center text-black">
|
||||
<Globe size={24} />
|
||||
</div>
|
||||
<h4 className="text-2xl font-bold text-slate-900">Bestehende Website</h4>
|
||||
<span className="px-2 py-1 bg-slate-50 text-slate-400 text-[10px] font-bold uppercase tracking-wider rounded">Optional</span>
|
||||
</div>
|
||||
<Input
|
||||
label="URL (falls vorhanden)"
|
||||
type="url"
|
||||
icon={Link2}
|
||||
placeholder="https://www.beispiel.de"
|
||||
value={state.existingWebsite}
|
||||
onChange={(e) => updateState({ existingWebsite: e.target.value })}
|
||||
/>
|
||||
</div>
|
||||
</Reveal>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
<Reveal width="100%" delay={0.2}>
|
||||
<Input
|
||||
label="Bestehende Domain"
|
||||
placeholder="z.B. beispiel.de"
|
||||
value={state.existingDomain}
|
||||
onChange={(e) => updateState({ existingDomain: e.target.value })}
|
||||
/>
|
||||
</Reveal>
|
||||
<Reveal width="100%" delay={0.2}>
|
||||
<Input
|
||||
label="Wunsch-Domain"
|
||||
placeholder="z.B. neue-marke.de"
|
||||
value={state.wishedDomain}
|
||||
onChange={(e) => updateState({ wishedDomain: e.target.value })}
|
||||
/>
|
||||
</Reveal>
|
||||
</div>
|
||||
|
||||
<Reveal width="100%" delay={0.3}>
|
||||
<div className="space-y-10">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-12 h-12 bg-slate-50 rounded-2xl flex items-center justify-center text-black">
|
||||
<Share2 size={24} />
|
||||
</div>
|
||||
<h4 className="text-2xl font-bold text-slate-900">Social Media Accounts</h4>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-5 gap-6">
|
||||
{SOCIAL_PLATFORMS.map((platform) => {
|
||||
const isSelected = state.socialMedia.includes(platform.id);
|
||||
const Icon = platform.icon;
|
||||
return (
|
||||
<motion.button
|
||||
key={platform.id}
|
||||
whileHover={{ y: -8, scale: 1.02 }}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
type="button"
|
||||
onClick={() => updateState({ socialMedia: toggleItem(state.socialMedia, platform.id) })}
|
||||
className={`flex flex-col items-center gap-4 p-8 rounded-[2.5rem] border-2 transition-all duration-500 ${
|
||||
isSelected ? 'border-slate-900 bg-slate-900 text-white shadow-2xl shadow-slate-400' : 'border-slate-100 bg-white text-slate-400 hover:border-slate-300 hover:shadow-xl'
|
||||
}`}
|
||||
>
|
||||
<div className={`p-4 rounded-2xl transition-colors duration-500 ${isSelected ? 'bg-white/10 text-white' : 'bg-slate-50 text-slate-400'}`}>
|
||||
<Icon size={32} />
|
||||
</div>
|
||||
<span className="font-bold text-base tracking-tight">{platform.label}</span>
|
||||
</motion.button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<AnimatePresence mode="popLayout">
|
||||
{state.socialMedia.map((id) => {
|
||||
const platform = SOCIAL_PLATFORMS.find(p => p.id === id);
|
||||
if (!platform) return null;
|
||||
return (
|
||||
<motion.div
|
||||
key={id}
|
||||
initial={{ opacity: 0, x: -20 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
exit={{ opacity: 0, x: 20 }}
|
||||
layout
|
||||
className="relative group"
|
||||
>
|
||||
<div className="absolute left-6 top-1/2 -translate-y-1/2 flex items-center gap-3 text-slate-400 group-focus-within:text-slate-900 transition-colors">
|
||||
<span className="font-bold text-xs uppercase tracking-widest w-20">{platform.label}</span>
|
||||
<div className="w-[1px] h-4 bg-slate-200" />
|
||||
<Link2 size={18} />
|
||||
</div>
|
||||
<input
|
||||
type="url"
|
||||
placeholder={`https://${platform.id}.com/ihr-profil`}
|
||||
value={state.socialMediaUrls[id] || ''}
|
||||
onChange={(e) => updateUrl(id, e.target.value)}
|
||||
className="w-full p-6 pl-40 bg-white border border-slate-100 rounded-[2rem] focus:outline-none focus:border-slate-900 transition-all duration-500 text-lg focus:shadow-xl"
|
||||
/>
|
||||
</motion.div>
|
||||
);
|
||||
})}
|
||||
</AnimatePresence>
|
||||
|
||||
{state.socialMedia.length === 0 && (
|
||||
<div className="p-12 border-2 border-dashed border-slate-100 rounded-[3rem] text-center">
|
||||
<p className="text-slate-400 font-medium">Wählen Sie oben Ihre Kanäle aus, um die Links hinzuzufügen.</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Reveal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user