diff --git a/components/atoms/form-field.tsx b/components/atoms/form-field.tsx new file mode 100644 index 0000000..e3e860b --- /dev/null +++ b/components/atoms/form-field.tsx @@ -0,0 +1,29 @@ +import type React from 'react'; +import { vstack } from 'styled-system/patterns'; +import { Text } from './text'; + +interface FormFieldProps { + label: string; + required?: boolean; + children: React.ReactNode; + className?: string; +} + +export function FormField({ + label, + required = false, + children, + className = '', +}: FormFieldProps) { + return ( +
+ + {label} + {required && '*'} + + {children} +
+ ); +} diff --git a/components/atoms/icon-box.tsx b/components/atoms/icon-box.tsx new file mode 100644 index 0000000..a9bdc6b --- /dev/null +++ b/components/atoms/icon-box.tsx @@ -0,0 +1,21 @@ +import type React from 'react'; +import type { IconBoxVariantProps } from 'styled-system/recipes'; +import { iconBox } from 'styled-system/recipes'; + +interface IconBoxProps extends IconBoxVariantProps { + children: React.ReactNode; + className?: string; +} + +export function IconBox({ + size, + variant, + color, + children, + className = '', +}: IconBoxProps) { + const recipeClasses = iconBox({ size, variant, color }); + const classes = className ? `${recipeClasses} ${className}` : recipeClasses; + + return
{children}
; +} diff --git a/components/atoms/text.tsx b/components/atoms/text.tsx new file mode 100644 index 0000000..76176a8 --- /dev/null +++ b/components/atoms/text.tsx @@ -0,0 +1,30 @@ +import type React from 'react'; +import type { TextVariantProps } from 'styled-system/recipes'; +import { text } from 'styled-system/recipes'; + +type TextElement = 'h1' | 'h2' | 'h3' | 'h4' | 'p' | 'span' | 'label'; + +interface TextProps extends TextVariantProps { + as?: TextElement; + children: React.ReactNode; + className?: string; + htmlFor?: string; +} + +export function Text({ + as: Component = 'p', + variant, + color, + children, + className = '', + htmlFor, +}: TextProps) { + const recipeClasses = text({ variant, color }); + const classes = className ? `${recipeClasses} ${className}` : recipeClasses; + + return ( + + {children} + + ); +} diff --git a/components/sections/partners/cta-section.tsx b/components/sections/partners/cta-section.tsx index 0b44241..f9101aa 100644 --- a/components/sections/partners/cta-section.tsx +++ b/components/sections/partners/cta-section.tsx @@ -1,6 +1,8 @@ import { css } from 'styled-system/css'; import { flex } from 'styled-system/patterns'; import { Button } from '../../atoms/Button'; +import { Text } from '../../atoms/text'; +import { Container } from '../../organisms/Container'; export function CtaSection() { return ( @@ -13,29 +15,25 @@ export function CtaSection() { overflow: 'hidden', })} > -
-

Besoin d'un architecte pour votre stack ? -

+

- + ); } diff --git a/components/sections/partners/ecosystem-section.tsx b/components/sections/partners/ecosystem-section.tsx index fc371c2..30da7a2 100644 --- a/components/sections/partners/ecosystem-section.tsx +++ b/components/sections/partners/ecosystem-section.tsx @@ -7,7 +7,7 @@ import { type PartnerCategory, partners, } from '../../../data/partners-data'; -import { PartnerCard } from './partner-card'; +import { PartnerCardFromData } from './partner-card'; function filterPartners(filter: 'TOUS' | PartnerCategory): Partner[] { const filtered = @@ -106,7 +106,7 @@ export function EcosystemSection() { className={`${grid({ columns: { base: 1, md: 2, lg: 3 }, gap: '8' })} ${css({ transition: 'all', transitionDuration: '500ms' })}`} > {filtered.map((partner) => ( - -
- - TECHNOLOGIE - - -

- Un écosystème
- de solutions{' '} - - connectées. - -

- -

+

- Nous maîtrisons les architectures technologiques les plus avancées - pour transformer vos outils en{' '} - - véritable levier de croissance. - -

-
+ + TECHNOLOGIE + -
- -
+ + Un écosystème
+ de solutions{' '} + + connectées. + +
-
- -
+ + Nous maîtrisons les architectures technologiques les plus avancées + pour transformer vos outils en{' '} + + véritable levier de croissance. + + +
-
- -
+
+ +
+ +
+ +
+ +
+ +
+ ); } diff --git a/components/sections/partners/partner-card.tsx b/components/sections/partners/partner-card.tsx index 2cfac24..dc9e380 100644 --- a/components/sections/partners/partner-card.tsx +++ b/components/sections/partners/partner-card.tsx @@ -1,287 +1,378 @@ import { Award, ExternalLink, Zap } from 'lucide-react'; +import type React from 'react'; import { css } from 'styled-system/css'; +import { styled } from 'styled-system/jsx'; import { center, flex, vstack } from 'styled-system/patterns'; import type { Partner } from '../../../data/partners-data'; -interface PartnerCardProps { - partner: Partner; - animate: boolean; +// ===== STYLE WRAPPER (local, not exported) ===== + +const PartnerCardWrapper = styled('div', { + base: { + display: 'flex', + flexDirection: 'column', + alignItems: 'stretch', + bg: 'white', + borderWidth: '1px', + borderColor: 'gray.100', + p: '8', + transition: 'all', + transitionDuration: '300ms', + position: 'relative', + overflow: 'hidden', + rounded: 'xl', + h: 'full', + _hover: { shadow: 'xl', transform: 'translateY(-4px)' }, + '& .logo-img': { + transition: 'all', + transitionDuration: '500ms', + }, + '&:hover .logo-img': { + filter: 'grayscale(0)', + opacity: 1, + }, + '& .cat-badge': { + transition: 'all', + }, + '&:hover .cat-badge': { + bg: 'ocobo.dark', + color: 'white', + }, + '& .tag': { + transition: 'all', + }, + '&:hover .tag': { + borderColor: 'ocobo.dark/10', + color: 'ocobo.dark', + }, + '& .separator': { + transition: 'opacity', + }, + '&:hover .separator': { + opacity: 1, + }, + '& .cert-img': { + transition: 'all', + transitionDuration: '500ms', + }, + '&:hover .cert-img': { + filter: 'grayscale(0)', + }, + '& .tech-label': { + transition: 'colors', + }, + '&:hover .tech-label': { + color: 'ocobo.mint', + }, + '& .tech-icon': { + transition: 'opacity', + }, + '&:hover .tech-icon': { + opacity: 1, + }, + '& .external-link': { + transition: 'all', + transitionDuration: '300ms', + }, + '&:hover .external-link': { + transform: 'translateX(4px)', + }, + }, + variants: { + animate: { + true: { + animation: 'fade-in-up-small', + opacity: 0, + }, + false: { + opacity: 1, + }, + }, + }, + defaultVariants: { + animate: false, + }, +}); + +// ===== ROOT ===== + +interface PartnerCardRootProps { + animate?: boolean; + children: React.ReactNode; + className?: string; +} + +function PartnerCardRoot({ + animate = false, + children, + className, +}: PartnerCardRootProps) { + return ( + + {children} + + ); +} + +// ===== HEADER ===== + +interface HeaderProps { + logo: string; + name: string; + categories: string[]; } -export function PartnerCard({ partner, animate }: PartnerCardProps) { +function Header({ logo, name, categories }: HeaderProps) { return (
-
- {partner.name} +
+
+ {categories.map((cat) => ( +
-
-
- {partner.category.map((cat) => ( -
- {cat === 'NO-CODE' ? 'No-Code & Automatisation' : cat} -
- ))} -
+ > + {cat === 'NO-CODE' ? 'No-Code & Automatisation' : cat} +
+ ))}
+
+ ); +} + +// ===== BODY ===== +interface BodyProps { + name: string; + tags: string[]; + description: string; +} + +function Body({ name, tags, description }: BodyProps) { + return ( +
+

+ {name} +

-

- {partner.name} -

-
- {partner.tags.map((tag) => ( - - {tag} - - ))} -
-

- {partner.desc} -

+ {tags.map((tag) => ( + + {tag} + + ))}
+

+ {description} +

+
+ ); +} - {/* Horizontal Black Separator */} -
+// ===== SEPARATOR ===== -
-
- {partner.certificationLogo ? ( -
- {`${partner.name} -
- ) : partner.status === 'OFFICIAL' ? ( -
+ ); +} + +// ===== FOOTER ===== + +interface FooterProps { + certificationLogo?: string; + name: string; + status: string; +} + +function Footer({ certificationLogo, name, status }: FooterProps) { + return ( +
+
+ {certificationLogo ? ( +
+ {`${name} +
+ ) : status === 'OFFICIAL' ? ( +
+ + Partenaire Officiel +
+ ) : ( +
- - Partenaire Officiel -
- ) : ( -
- - Maîtrise Technique -
- )} -
- - - - + color: 'gray.400', + }, + )}`} + > + + Maîtrise Technique +
+ )}
+ + + +
); } + +// ===== COMPOUND EXPORT ===== + +export const PartnerCard = Object.assign(PartnerCardRoot, { + Header, + Body, + Separator, + Footer, +}); + +// ===== CONVENIENCE COMPONENT ===== + +interface PartnerCardFromDataProps { + partner: Partner; + animate?: boolean; +} + +export function PartnerCardFromData({ + partner, + animate = false, +}: PartnerCardFromDataProps) { + return ( + + + + + + + ); +} diff --git a/components/sections/partners/partner-form.tsx b/components/sections/partners/partner-form.tsx index 2bdf0fa..68df6c1 100644 --- a/components/sections/partners/partner-form.tsx +++ b/components/sections/partners/partner-form.tsx @@ -4,7 +4,11 @@ import { useState } from 'react'; import { css } from 'styled-system/css'; import { center, flex, grid, vstack } from 'styled-system/patterns'; import { Badge } from '../../atoms/Badge'; +import { FormField } from '../../atoms/form-field'; +import { IconBox } from '../../atoms/icon-box'; +import { Text } from '../../atoms/text'; import FlexPair from '../../layout/FlexPair'; +import { Container } from '../../organisms/Container'; function BenefitItem({ icon, @@ -27,9 +31,9 @@ function BenefitItem({ > {title} -

+ {description} -

+ ); @@ -57,20 +61,17 @@ function SuccessMessage({ onReset }: { onReset: () => void }) { >
-

Demande reçue ! -

-

+ + Notre équipe étudiera votre solution et vous recontactera sous 48h. -

+