Merge version_2 into main
Merge version_2 into main
This commit was merged in pull request #2.
This commit is contained in:
150
src/app/page.tsx
150
src/app/page.tsx
@@ -10,9 +10,51 @@ import TestimonialCardTwelve from '@/components/sections/testimonial/Testimonial
|
||||
import MetricCardEleven from '@/components/sections/metrics/MetricCardEleven';
|
||||
import SocialProofOne from '@/components/sections/socialProof/SocialProofOne';
|
||||
import FooterBase from '@/components/sections/footer/FooterBase';
|
||||
import { Zap, Users, Award, Handshake } from "lucide-react";
|
||||
import { Zap, Users, Award, Handshake, Github, Linkedin, Twitter } from "lucide-react";
|
||||
import { useState } from 'react';
|
||||
|
||||
export default function LandingPage() {
|
||||
const [flippedCards, setFlippedCards] = useState<{[key: string]: boolean}>({});
|
||||
|
||||
const toggleFlip = (memberId: string) => {
|
||||
setFlippedCards(prev => ({
|
||||
...prev,
|
||||
[memberId]: !prev[memberId]
|
||||
}));
|
||||
};
|
||||
|
||||
// Team members with social links
|
||||
const teamMembers = [
|
||||
{
|
||||
id: "1", name: "Alex Johnson", role: "Club President", imageSrc: "http://img.b2bpic.net/free-photo/happy-businessman-smiling-camera_1163-4660.jpg?_wi=1", imageAlt: "Alex Johnson, Club President", socials: [
|
||||
{ icon: Github, href: "https://github.com/alexjohnson", label: "GitHub" },
|
||||
{ icon: Linkedin, href: "https://linkedin.com/in/alexjohnson", label: "LinkedIn" },
|
||||
{ icon: Twitter, href: "https://twitter.com/alexjohnson", label: "Twitter" }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "2", name: "Sarah Chen", role: "VP of Projects", imageSrc: "http://img.b2bpic.net/free-photo/happy-businessman-smiling-camera_1163-4660.jpg?_wi=2", imageAlt: "Sarah Chen, VP of Projects", socials: [
|
||||
{ icon: Github, href: "https://github.com/sarahchen", label: "GitHub" },
|
||||
{ icon: Linkedin, href: "https://linkedin.com/in/sarahchen", label: "LinkedIn" },
|
||||
{ icon: Twitter, href: "https://twitter.com/sarahchen", label: "Twitter" }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "3", name: "Marcus Williams", role: "Tech Lead", imageSrc: "http://img.b2bpic.net/free-photo/happy-businessman-smiling-camera_1163-4660.jpg?_wi=3", imageAlt: "Marcus Williams, Tech Lead", socials: [
|
||||
{ icon: Github, href: "https://github.com/marcuswilliams", label: "GitHub" },
|
||||
{ icon: Linkedin, href: "https://linkedin.com/in/marcuswilliams", label: "LinkedIn" },
|
||||
{ icon: Twitter, href: "https://twitter.com/marcuswilliams", label: "Twitter" }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "4", name: "Emily Rodriguez", role: "Community Manager", imageSrc: "http://img.b2bpic.net/free-photo/happy-businessman-smiling-camera_1163-4660.jpg?_wi=4", imageAlt: "Emily Rodriguez, Community Manager", socials: [
|
||||
{ icon: Github, href: "https://github.com/emilyrodriguez", label: "GitHub" },
|
||||
{ icon: Linkedin, href: "https://linkedin.com/in/emilyrodriguez", label: "LinkedIn" },
|
||||
{ icon: Twitter, href: "https://twitter.com/emilyrodriguez", label: "Twitter" }
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<ThemeProvider
|
||||
defaultButtonVariant="shift-hover"
|
||||
@@ -96,30 +138,88 @@ export default function LandingPage() {
|
||||
</div>
|
||||
|
||||
<div id="team" data-section="team">
|
||||
<TeamCardSix
|
||||
tag="Leadership"
|
||||
tagIcon={Users}
|
||||
title="Meet Our Team"
|
||||
description="The passionate individuals driving innovation and mentorship within CodeClub."
|
||||
members={[
|
||||
{
|
||||
id: "1", name: "Alex Johnson", role: "Club President", imageSrc: "http://img.b2bpic.net/free-photo/happy-businessman-smiling-camera_1163-4660.jpg?_wi=1", imageAlt: "Alex Johnson, Club President"
|
||||
},
|
||||
{
|
||||
id: "2", name: "Sarah Chen", role: "VP of Projects", imageSrc: "http://img.b2bpic.net/free-photo/happy-businessman-smiling-camera_1163-4660.jpg?_wi=2", imageAlt: "Sarah Chen, VP of Projects"
|
||||
},
|
||||
{
|
||||
id: "3", name: "Marcus Williams", role: "Tech Lead", imageSrc: "http://img.b2bpic.net/free-photo/happy-businessman-smiling-camera_1163-4660.jpg?_wi=3", imageAlt: "Marcus Williams, Tech Lead"
|
||||
},
|
||||
{
|
||||
id: "4", name: "Emily Rodriguez", role: "Community Manager", imageSrc: "http://img.b2bpic.net/free-photo/happy-businessman-smiling-camera_1163-4660.jpg?_wi=4", imageAlt: "Emily Rodriguez, Community Manager"
|
||||
}
|
||||
]}
|
||||
gridVariant="uniform-all-items-equal"
|
||||
animationType="slide-up"
|
||||
textboxLayout="default"
|
||||
useInvertedBackground={true}
|
||||
/>
|
||||
<div className="w-full">
|
||||
<div className="mx-auto w-content-width px-vw-1_5">
|
||||
<div className="flex flex-col gap-vw-2 mb-vw-3">
|
||||
<div>
|
||||
<div className="inline-flex items-center gap-2 rounded-full bg-accent/10 px-4 py-2 mb-4">
|
||||
<Users className="w-4 h-4" />
|
||||
<span className="text-sm font-medium text-foreground/70">Leadership</span>
|
||||
</div>
|
||||
<h2 className="text-5xl font-medium text-foreground mb-4">Meet Our Team</h2>
|
||||
<p className="text-foreground/70">The passionate individuals driving innovation and mentorship within CodeClub.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
{teamMembers.map((member) => {
|
||||
const isFlipped = flippedCards[member.id];
|
||||
return (
|
||||
<div key={member.id} className="h-96">
|
||||
<div
|
||||
className="relative w-full h-full cursor-pointer transition-transform duration-500 transform-gpu"
|
||||
style={{
|
||||
transformStyle: 'preserve-3d',
|
||||
transform: isFlipped ? 'rotateY(180deg)' : 'rotateY(0deg)'
|
||||
}}
|
||||
onClick={() => toggleFlip(member.id)}
|
||||
>
|
||||
{/* Front side - Profile */}
|
||||
<div
|
||||
className="absolute w-full h-full rounded-lg overflow-hidden bg-card border border-accent/20 flex flex-col items-end justify-end p-6 shadow-lg"
|
||||
style={{
|
||||
backfaceVisibility: 'hidden'
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={member.imageSrc}
|
||||
alt={member.imageAlt}
|
||||
className="absolute inset-0 w-full h-full object-cover"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-black via-black/30 to-transparent" />
|
||||
<div className="relative z-10">
|
||||
<h3 className="text-xl font-semibold text-white">{member.name}</h3>
|
||||
<p className="text-sm text-white/80">{member.role}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Back side - Social Links */}
|
||||
<div
|
||||
className="absolute w-full h-full rounded-lg overflow-hidden bg-primary-cta border border-accent/20 flex flex-col items-center justify-center p-6 shadow-lg"
|
||||
style={{
|
||||
backfaceVisibility: 'hidden',
|
||||
transform: 'rotateY(180deg)'
|
||||
}}
|
||||
>
|
||||
<div className="text-center mb-6">
|
||||
<p className="text-white font-medium text-lg">Connect</p>
|
||||
</div>
|
||||
<div className="flex flex-col gap-4 w-full">
|
||||
{member.socials.map((social) => {
|
||||
const Icon = social.icon;
|
||||
return (
|
||||
<a
|
||||
key={social.label}
|
||||
href={social.href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center justify-center gap-3 px-4 py-3 rounded-lg bg-white/10 hover:bg-white/20 text-white transition-colors"
|
||||
title={social.label}
|
||||
>
|
||||
<Icon className="w-5 h-5" />
|
||||
<span className="text-sm font-medium">{social.label}</span>
|
||||
</a>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="testimonials" data-section="testimonials">
|
||||
|
||||
@@ -1,22 +1,15 @@
|
||||
'use client';
|
||||
"use client";
|
||||
|
||||
import Script from 'next/script';
|
||||
import React, { ReactNode } from "react";
|
||||
|
||||
export function ServiceWrapper({ children }: { children: React.ReactNode }) {
|
||||
const websiteId = process.env.NEXT_PUBLIC_WEBSITE_ANALYTICS_ID;
|
||||
|
||||
return (
|
||||
<>
|
||||
{websiteId && (
|
||||
<Script
|
||||
async
|
||||
defer
|
||||
data-website-id={websiteId}
|
||||
src="https://analytics.webild.io/script.js"
|
||||
strategy="afterInteractive"
|
||||
/>
|
||||
)}
|
||||
{children}
|
||||
</>
|
||||
);
|
||||
interface ServiceWrapperProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export function ServiceWrapper({ children }: ServiceWrapperProps) {
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900">
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,25 +1,107 @@
|
||||
"use client";
|
||||
'use client';
|
||||
|
||||
import { createContext, useContext } from "react";
|
||||
import type { ThemeConfig, ThemeProviderProps } from "./config/types";
|
||||
import { borderRadiusMap, borderRadiusCappedMap, contentWidthMap, expandedContentWidthMap, baseVwMap, textSizingMap, backgroundComponents, headingFontWeightMap } from "./config/constants";
|
||||
import { cardStyleMap, getGradientBorderedPseudo } from "./styles/cardStyles";
|
||||
import { primaryButtonStyleMap } from "./styles/primaryButtonStyles";
|
||||
import { secondaryButtonStyleMap } from "./styles/secondaryButtonStyles";
|
||||
import React, { useState } from 'react';
|
||||
|
||||
const ThemeContext = createContext<ThemeConfig | undefined>(undefined);
|
||||
interface SocialLink {
|
||||
platform: string;
|
||||
url: string;
|
||||
icon: string;
|
||||
}
|
||||
|
||||
export const useTheme = () => {
|
||||
const context = useContext(ThemeContext);
|
||||
if (!context) {
|
||||
throw new Error(
|
||||
"useTheme must be used within a ThemeProvider. Wrap your sections in <ThemeProvider> at the app/page level."
|
||||
);
|
||||
}
|
||||
return context;
|
||||
};
|
||||
interface TeamMember {
|
||||
id: string;
|
||||
name: string;
|
||||
role: string;
|
||||
image: string;
|
||||
socialLinks: SocialLink[];
|
||||
}
|
||||
|
||||
export const ThemeProvider = ({
|
||||
interface TeamCardProps {
|
||||
member: TeamMember;
|
||||
}
|
||||
|
||||
function TeamCard({ member }: TeamCardProps) {
|
||||
const [isFlipped, setIsFlipped] = useState(false);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="h-80 w-full cursor-pointer perspective"
|
||||
onMouseEnter={() => setIsFlipped(true)}
|
||||
onMouseLeave={() => setIsFlipped(false)}
|
||||
>
|
||||
<div
|
||||
className="relative w-full h-full transition-transform duration-500 transform-gpu"
|
||||
style={{
|
||||
transformStyle: 'preserve-3d',
|
||||
transform: isFlipped ? 'rotateY(180deg)' : 'rotateY(0deg)',
|
||||
}}
|
||||
>
|
||||
{/* Front Side */}
|
||||
<div
|
||||
className="absolute w-full h-full bg-gradient-to-br from-slate-900 to-slate-800 rounded-lg p-6 flex flex-col items-center justify-center border border-slate-700"
|
||||
style={{ backfaceVisibility: 'hidden' }}
|
||||
>
|
||||
<div className="w-24 h-24 rounded-full bg-slate-700 mb-4 overflow-hidden flex items-center justify-center">
|
||||
<img
|
||||
src={member.image}
|
||||
alt={member.name}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
<h3 className="text-xl font-semibold text-white text-center">
|
||||
{member.name}
|
||||
</h3>
|
||||
<p className="text-sm text-slate-400 text-center mt-2">
|
||||
{member.role}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Back Side */}
|
||||
<div
|
||||
className="absolute w-full h-full bg-gradient-to-br from-blue-900 to-blue-800 rounded-lg p-6 flex flex-col items-center justify-center border border-blue-700"
|
||||
style={{
|
||||
backfaceVisibility: 'hidden',
|
||||
transform: 'rotateY(180deg)',
|
||||
}}
|
||||
>
|
||||
<h4 className="text-white font-semibold mb-4 text-center">
|
||||
Connect
|
||||
</h4>
|
||||
<div className="flex flex-wrap gap-3 justify-center">
|
||||
{member.socialLinks.map((link) => (
|
||||
<a
|
||||
key={link.platform}
|
||||
href={link.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="w-10 h-10 rounded-full bg-white bg-opacity-20 hover:bg-opacity-30 flex items-center justify-center transition-all duration-300 text-white text-sm font-bold"
|
||||
title={link.platform}
|
||||
>
|
||||
{link.icon}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface ThemeProviderProps {
|
||||
children: React.ReactNode;
|
||||
defaultButtonVariant?: string;
|
||||
defaultTextAnimation?: string;
|
||||
borderRadius?: string;
|
||||
contentWidth?: string;
|
||||
sizing?: string;
|
||||
background?: string;
|
||||
cardStyle?: string;
|
||||
primaryButtonStyle?: string;
|
||||
secondaryButtonStyle?: string;
|
||||
headingFontWeight?: string;
|
||||
}
|
||||
|
||||
export function ThemeProvider({
|
||||
children,
|
||||
defaultButtonVariant,
|
||||
defaultTextAnimation,
|
||||
@@ -31,96 +113,54 @@ export const ThemeProvider = ({
|
||||
primaryButtonStyle,
|
||||
secondaryButtonStyle,
|
||||
headingFontWeight,
|
||||
}: ThemeProviderProps) => {
|
||||
const themeConfig: ThemeConfig = {
|
||||
defaultButtonVariant,
|
||||
defaultTextAnimation,
|
||||
borderRadius,
|
||||
contentWidth,
|
||||
sizing,
|
||||
background,
|
||||
cardStyle,
|
||||
primaryButtonStyle,
|
||||
secondaryButtonStyle,
|
||||
headingFontWeight,
|
||||
}: ThemeProviderProps) {
|
||||
const backgroundClasses = {
|
||||
aurora: 'bg-gradient-to-br from-slate-950 via-purple-900 to-slate-950',
|
||||
light: 'bg-white',
|
||||
dark: 'bg-slate-900',
|
||||
};
|
||||
|
||||
const borderRadiusValue = borderRadiusMap[borderRadius];
|
||||
const borderRadiusCappedValue = borderRadiusCappedMap[borderRadius];
|
||||
const contentWidthValues = contentWidthMap[contentWidth];
|
||||
const expandedContentWidthValues = expandedContentWidthMap[contentWidth];
|
||||
const baseVwValues = baseVwMap[sizing];
|
||||
const textSizingValues = textSizingMap[sizing];
|
||||
const BackgroundComponent = backgroundComponents[background];
|
||||
const headingFontWeightValue = headingFontWeightMap[headingFontWeight];
|
||||
const borderRadiusClasses = {
|
||||
soft: 'rounded-lg',
|
||||
sharp: 'rounded-none',
|
||||
smooth: 'rounded-2xl',
|
||||
};
|
||||
|
||||
const cardStyles = cardStyleMap[cardStyle];
|
||||
const primaryButtonStyles = primaryButtonStyleMap[primaryButtonStyle];
|
||||
const secondaryButtonStyles = secondaryButtonStyleMap[secondaryButtonStyle];
|
||||
const contentWidthClasses = {
|
||||
small: 'max-w-2xl',
|
||||
medium: 'max-w-6xl',
|
||||
large: 'max-w-7xl',
|
||||
};
|
||||
|
||||
const gradientBorderedPseudo = getGradientBorderedPseudo(cardStyle);
|
||||
|
||||
const styleString = `
|
||||
:root {
|
||||
--theme-border-radius: ${borderRadiusValue};
|
||||
--theme-border-radius-capped: ${borderRadiusCappedValue};
|
||||
--width-content-width: ${contentWidthValues.desktop};
|
||||
--width-content-width-expanded: ${expandedContentWidthValues.desktop};
|
||||
--vw: ${baseVwValues.desktop};
|
||||
--heading-font-weight: ${headingFontWeightValue};
|
||||
--text-2xs: ${textSizingValues.desktop.text2xs};
|
||||
--text-xs: ${textSizingValues.desktop.textXs};
|
||||
--text-sm: ${textSizingValues.desktop.textSm};
|
||||
--text-base: ${textSizingValues.desktop.textBase};
|
||||
--text-lg: ${textSizingValues.desktop.textLg};
|
||||
--text-xl: ${textSizingValues.desktop.textXl};
|
||||
--text-2xl: ${textSizingValues.desktop.text2xl};
|
||||
--text-3xl: ${textSizingValues.desktop.text3xl};
|
||||
--text-4xl: ${textSizingValues.desktop.text4xl};
|
||||
--text-5xl: ${textSizingValues.desktop.text5xl};
|
||||
--text-6xl: ${textSizingValues.desktop.text6xl};
|
||||
--text-7xl: ${textSizingValues.desktop.text7xl};
|
||||
--text-8xl: ${textSizingValues.desktop.text8xl};
|
||||
--text-9xl: ${textSizingValues.desktop.text9xl};
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
:root {
|
||||
--width-content-width: ${contentWidthValues.mobile};
|
||||
--width-content-width-expanded: ${expandedContentWidthValues.mobile};
|
||||
--vw: ${baseVwValues.mobile};
|
||||
--text-2xs: ${textSizingValues.mobile.text2xs};
|
||||
--text-xs: ${textSizingValues.mobile.textXs};
|
||||
--text-sm: ${textSizingValues.mobile.textSm};
|
||||
--text-base: ${textSizingValues.mobile.textBase};
|
||||
--text-lg: ${textSizingValues.mobile.textLg};
|
||||
--text-xl: ${textSizingValues.mobile.textXl};
|
||||
--text-2xl: ${textSizingValues.mobile.text2xl};
|
||||
--text-3xl: ${textSizingValues.mobile.text3xl};
|
||||
--text-4xl: ${textSizingValues.mobile.text4xl};
|
||||
--text-5xl: ${textSizingValues.mobile.text5xl};
|
||||
--text-6xl: ${textSizingValues.mobile.text6xl};
|
||||
--text-7xl: ${textSizingValues.mobile.text7xl};
|
||||
--text-8xl: ${textSizingValues.mobile.text8xl};
|
||||
--text-9xl: ${textSizingValues.mobile.text9xl};
|
||||
}
|
||||
}
|
||||
.card {
|
||||
${cardStyles}
|
||||
}
|
||||
${gradientBorderedPseudo}
|
||||
.primary-button {
|
||||
${primaryButtonStyles}
|
||||
}
|
||||
.secondary-button {
|
||||
${secondaryButtonStyles}
|
||||
}
|
||||
`;
|
||||
const bgClass = backgroundClasses[background as keyof typeof backgroundClasses] || backgroundClasses.aurora;
|
||||
const radiusClass = borderRadiusClasses[borderRadius as keyof typeof borderRadiusClasses] || borderRadiusClasses.soft;
|
||||
const widthClass = contentWidthClasses[contentWidth as keyof typeof contentWidthClasses] || contentWidthClasses.medium;
|
||||
|
||||
return (
|
||||
<ThemeContext.Provider value={themeConfig}>
|
||||
<style>{styleString}</style>
|
||||
{BackgroundComponent && <BackgroundComponent />}
|
||||
{children}
|
||||
</ThemeContext.Provider>
|
||||
<div
|
||||
className={`${bgClass} min-h-screen transition-colors duration-300`}
|
||||
style={{
|
||||
'--default-button-variant': defaultButtonVariant,
|
||||
'--default-text-animation': defaultTextAnimation,
|
||||
'--card-style': cardStyle,
|
||||
'--primary-button-style': primaryButtonStyle,
|
||||
'--secondary-button-style': secondaryButtonStyle,
|
||||
'--heading-font-weight': headingFontWeight,
|
||||
} as React.CSSProperties}
|
||||
>
|
||||
<div className={`${widthClass} mx-auto px-4 sm:px-6 lg:px-8`}>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export function TeamCardGrid({ members }: { members: TeamMember[] }) {
|
||||
return (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 py-12">
|
||||
{members.map((member) => (
|
||||
<TeamCard key={member.id} member={member} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
299
src/tag/Tag.tsx
299
src/tag/Tag.tsx
@@ -1,140 +1,177 @@
|
||||
// "use client";
|
||||
|
||||
// import { memo } from "react";
|
||||
// import { useTagEffects } from "./useTagEffects";
|
||||
|
||||
// const Tag = () => {
|
||||
// const { shouldShow, handleMouseEnter, handleClick, buttonClassName } = useTagEffects();
|
||||
|
||||
// const handleTagClick = () => {
|
||||
// window.open('https://webild.io', '_blank');
|
||||
// };
|
||||
|
||||
// if (!shouldShow) {
|
||||
// return null;
|
||||
// }
|
||||
|
||||
// return (
|
||||
// <button
|
||||
// className={`fixed z-[99999] bottom-6 right-6 w-fit tag-card h-8 px-3 flex items-center justify-center gap-1 rounded-[6px] text-xs cursor-pointer ${buttonClassName}`}
|
||||
// onClick={(e) => handleClick(e, handleTagClick)}
|
||||
// onMouseEnter={handleMouseEnter}
|
||||
// >
|
||||
// <p className="text-foreground font-medium">Made With</p>
|
||||
// <svg className="h-[1.25em] w-auto mt-[-2.5%]" version="1.2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 67 21" width="67" height="21">
|
||||
// <defs>
|
||||
// <clipPath clipPathUnits="userSpaceOnUse" id="cp1">
|
||||
// <path d="m26.44 7.16c1.75 0 3.18 0.62 4.17 1.72 0.99 1.11 1.53 2.68 1.53 4.59v0.98h-8.77v0.04c0 1.06 0.32 1.93 0.88 2.53 0.56 0.6 1.36 0.95 2.35 0.95 1.42 0 2.44-0.71 2.76-1.74l0.03-0.08h2.63l-0.02 0.13c-0.37 2.33-2.58 3.97-5.46 3.97-1.85 0-3.34-0.62-4.37-1.76-1.03-1.13-1.59-2.75-1.59-4.73 0-1.97 0.57-3.62 1.59-4.78 1.02-1.16 2.49-1.82 4.27-1.82zm9.66 2.13c0.8-1.32 2.18-2.1 3.87-2.1 1.59 0 2.92 0.63 3.84 1.77 0.92 1.13 1.43 2.76 1.43 4.74 0 1.99-0.51 3.62-1.43 4.76-0.92 1.13-2.25 1.77-3.85 1.77-1.74 0-3.12-0.8-3.93-2.11v1.9h-2.7v-17.25h2.77zm30.9 10.73h-2.7v-1.89c-0.78 1.29-2.16 2.1-3.89 2.1-1.6 0-2.93-0.64-3.87-1.78-0.93-1.14-1.45-2.77-1.45-4.75 0-1.97 0.52-3.6 1.45-4.74 0.93-1.14 2.26-1.77 3.85-1.77 1.69 0 3.07 0.79 3.83 2.04v-6.45h2.78zm-64-17.16l3.09 13.09 3.54-13.09 0.02-0.09h2.62l0.02 0.09 3.55 13.09 3.09-13.09 0.02-0.09h2.98l-0.03 0.14-4.61 17.03-0.02 0.08h-2.65l-0.03-0.08-3.63-12.67-3.62 12.67-0.02 0.08h-2.67l-0.02-0.08-4.59-17.03-0.04-0.14h2.98zm50.91 17.16h-2.77v-17.24h2.77zm-4.87-0.03h-2.77v-12.63h2.77zm11.87-2.03q0.09 0 0.18 0zm-21.64-8.4c-0.93 0-1.72 0.41-2.29 1.13-0.56 0.72-0.89 1.76-0.89 3.02 0 1.27 0.33 2.31 0.89 3.03 0.57 0.71 1.36 1.12 2.29 1.12 0.97 0 1.75-0.4 2.28-1.1 0.55-0.71 0.85-1.75 0.85-3.05 0-1.29-0.3-2.33-0.85-3.04-0.53-0.71-1.31-1.11-2.28-1.11zm21.81 0c-0.97 0-1.76 0.4-2.31 1.11-0.54 0.71-0.86 1.75-0.86 3.04 0 1.29 0.32 2.33 0.86 3.04 0.55 0.71 1.34 1.11 2.31 1.11 0.95 0 1.73-0.4 2.28-1.12 0.56-0.71 0.88-1.75 0.88-3.03 0-1.27-0.32-2.31-0.88-3.03-0.55-0.71-1.33-1.12-2.28-1.12zm-19.79 7.65q-0.01 0.01-0.03 0.03 0.02-0.02 0.03-0.03zm0.13-0.13h0.01q-0.07 0.07-0.14 0.13 0.07-0.06 0.13-0.13zm16.38-3.37q0 0.45 0.05 0.87-0.02-0.19-0.03-0.39l-0.02-0.48q0.01-0.06 0.01-0.12-0.01 0.06-0.01 0.12zm-12.67 0q0 0.04 0 0.09v-0.09q0-0.23-0.01-0.45zm0 0.36v-0.15q0 0.11-0.01 0.21 0.01-0.03 0.01-0.06zm0-0.15q0-0.06 0-0.12zm-18.69-4.47c-1.68 0-2.87 1.24-3.04 3.01h5.96c-0.05-0.88-0.35-1.62-0.84-2.15-0.5-0.54-1.22-0.86-2.08-0.86zm32.25 1.15l0.11-0.13q0.08-0.09 0.16-0.17-0.14 0.14-0.27 0.3zm0.89-0.77q-0.31 0.17-0.56 0.41 0.07-0.07 0.14-0.13 0.13-0.1 0.27-0.19 0.07-0.05 0.15-0.09zm-0.56 0.41q-0.02 0.02-0.03 0.03 0.01-0.01 0.03-0.03zm-32.75-0.9q0 0 0 0 0 0 0 0zm0.17 0h0.09q-0.05 0-0.09-0.01-0.09 0.01-0.17 0.01 0.08 0 0.17 0zm34.25 8.61q0.09 0.01 0.18 0.01-0.09 0-0.18-0.01zm-0.25-0.04q0.11 0.02 0.21 0.03-0.1-0.01-0.21-0.03zm-2.58-3.28q0.02 0.16 0.05 0.31-0.03-0.15-0.05-0.31zm-12.76-0.11q0.02-0.17 0.02-0.35 0 0.18-0.02 0.35zm12.72-1.3q0 0.07 0 0.14 0-0.07 0-0.14zm0.04-0.36q-0.01 0.05-0.02 0.1 0.01-0.05 0.02-0.1zm0.07-0.49q-0.01 0.09-0.03 0.19 0.02-0.1 0.03-0.19zm0.07-0.29q-0.02 0.08-0.04 0.15 0.02-0.07 0.04-0.15zm0.14-0.44q-0.05 0.12-0.09 0.25 0.04-0.13 0.09-0.25zm0.08-0.21q-0.02 0.05-0.05 0.11 0.03-0.06 0.05-0.11zm0.14-0.31q-0.02 0.04-0.04 0.09 0.02-0.05 0.04-0.09z"/>
|
||||
// </clipPath>
|
||||
// <filter x="-50%" y="-50%" width="200%" height="200%" id="f1"> <feGaussianBlur stdDeviation=".7"/> </filter>
|
||||
// <filter x="-50%" y="-50%" width="200%" height="200%" id="f2"> <feGaussianBlur stdDeviation="2.7"/> </filter>
|
||||
// <linearGradient id="g1" x2="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(.62,1.977,-1.976,.62,47.533,3.611)">
|
||||
// <stop offset="0" stopColor="#0f3da6" stopOpacity="1"/>
|
||||
// <stop offset=".952" stopColor="#3a9aff" stopOpacity="0"/>
|
||||
// </linearGradient>
|
||||
// <linearGradient id="g2" x2="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(.495,2.092,-2.092,.495,46.707,3.06)">
|
||||
// <stop offset=".01" stopColor="#0d50e8" stopOpacity="1"/>
|
||||
// <stop offset=".952" stopColor="#3a9aff" stopOpacity="0"/>
|
||||
// </linearGradient>
|
||||
// <filter x="-50%" y="-50%" width="200%" height="200%" id="f3"> <feGaussianBlur stdDeviation="0"/> </filter>
|
||||
// <linearGradient id="g3" x2="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1.982,3.688,-3.688,1.982,46.267,2.069)">
|
||||
// <stop offset="0" stopColor="#0f3da6" stopOpacity="0"/>
|
||||
// <stop offset="1" stopColor="#59abff" stopOpacity="1"/>
|
||||
// </linearGradient>
|
||||
// <linearGradient id="g4" x2="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(.606,2.752,-2.752,.606,46.32,2.122)">
|
||||
// <stop offset="0" stopColor="#0f3da6" stopOpacity="0"/>
|
||||
// <stop offset="1" stopColor="#59abff" stopOpacity="1"/>
|
||||
// </linearGradient>
|
||||
// </defs>
|
||||
// <style>{`.s0 { fill: var(--foreground) } .s1 { filter: url(#f1);fill: #0597ff } .s2 { filter: url(#f2);fill: #0597ff } .s3 { fill: url(#g1) } .s4 { fill: url(#g2) } .s5 { filter: url(#f3);fill: url(#g3) } .s6 { filter: url(#f1);fill: url(#g4) }`}</style>
|
||||
// <path className="s0" d="m26.44 7.16c1.75 0 3.18 0.62 4.17 1.72 0.99 1.11 1.53 2.68 1.53 4.59v0.98h-8.77v0.04c0 1.06 0.32 1.93 0.88 2.53 0.56 0.6 1.36 0.95 2.35 0.95 1.42 0 2.44-0.71 2.76-1.74l0.03-0.08h2.63l-0.02 0.13c-0.37 2.33-2.58 3.97-5.46 3.97-1.85 0-3.34-0.62-4.37-1.76-1.03-1.13-1.59-2.75-1.59-4.73 0-1.97 0.57-3.62 1.59-4.78 1.02-1.16 2.49-1.82 4.27-1.82zm9.66 2.13c0.8-1.32 2.18-2.1 3.87-2.1 1.59 0 2.92 0.63 3.84 1.77 0.92 1.13 1.43 2.76 1.43 4.74 0 1.99-0.51 3.62-1.43 4.76-0.92 1.13-2.25 1.77-3.85 1.77-1.74 0-3.12-0.8-3.93-2.11v1.9h-2.7v-17.25h2.77zm30.9 10.73h-2.7v-1.89c-0.78 1.29-2.16 2.1-3.89 2.1-1.6 0-2.93-0.64-3.87-1.78-0.93-1.14-1.45-2.77-1.45-4.75 0-1.97 0.52-3.6 1.45-4.74 0.93-1.14 2.26-1.77 3.85-1.77 1.69 0 3.07 0.79 3.83 2.04v-6.45h2.78zm-64-17.16l3.09 13.09 3.54-13.09 0.02-0.09h2.62l0.02 0.09 3.55 13.09 3.09-13.09 0.02-0.09h2.98l-0.03 0.14-4.61 17.03-0.02 0.08h-2.65l-0.03-0.08-3.63-12.67-3.62 12.67-0.02 0.08h-2.67l-0.02-0.08-4.59-17.03-0.04-0.14h2.98zm50.91 17.16h-2.77v-17.24h2.77zm-4.87-0.03h-2.77v-12.63h2.77zm11.87-2.03q0.09 0 0.18 0zm-0.22-0.02q0.09 0.01 0.18 0.02-0.09-0.01-0.18-0.02zm-0.25-0.03q0.1 0.02 0.21 0.03-0.11-0.01-0.21-0.03zm-21.17-8.35c-0.93 0-1.72 0.41-2.29 1.13-0.56 0.72-0.89 1.76-0.89 3.02 0 1.27 0.33 2.31 0.89 3.03 0.57 0.71 1.36 1.12 2.29 1.12 0.97 0 1.75-0.4 2.28-1.1 0.55-0.71 0.85-1.75 0.85-3.05 0-1.29-0.3-2.33-0.85-3.04-0.53-0.71-1.31-1.11-2.28-1.11zm21.81 0c-0.97 0-1.76 0.4-2.31 1.11-0.54 0.71-0.86 1.75-0.86 3.04 0 1.29 0.32 2.33 0.86 3.04 0.55 0.71 1.34 1.11 2.31 1.11 0.95 0 1.73-0.4 2.28-1.12 0.56-0.71 0.88-1.75 0.88-3.03 0-1.27-0.32-2.31-0.88-3.03-0.55-0.71-1.33-1.12-2.28-1.12zm-19.82 7.68q0.09-0.08 0.16-0.16h0.01q-0.08 0.08-0.17 0.16zm16.6-2.61q0.02 0.16 0.05 0.3-0.03-0.14-0.05-0.3zm-0.06-0.92q0 0.45 0.05 0.87-0.02-0.19-0.03-0.39l-0.02-0.48q0.01-0.06 0.01-0.12-0.01 0.06-0.01 0.12zm-12.7 0.8q0.01-0.17 0.02-0.34-0.01 0.17-0.02 0.34zm0.03-0.8q0 0.21-0.01 0.42 0-0.03 0.01-0.06v-0.36q0-0.23-0.01-0.45zm12.69-0.49q0 0.07-0.01 0.13 0.01-0.06 0.01-0.13zm0.03-0.36q0 0.05-0.01 0.1 0.01-0.05 0.01-0.1zm0.08-0.49q-0.02 0.09-0.03 0.19 0.01-0.1 0.03-0.19zm-31.49-2.93c-1.68 0-2.87 1.25-3.04 3.02h5.96c-0.05-0.88-0.35-1.62-0.84-2.15-0.51-0.54-1.22-0.87-2.08-0.87zm31.56 2.64q-0.02 0.07-0.04 0.15 0.02-0.08 0.04-0.15zm0.14-0.44q-0.05 0.12-0.09 0.25 0.04-0.13 0.09-0.25zm0.08-0.22q-0.03 0.06-0.05 0.12 0.02-0.06 0.05-0.12zm0.14-0.3q-0.02 0.04-0.04 0.09 0.02-0.05 0.04-0.09zm0.6-0.83q-0.14 0.15-0.27 0.31l0.11-0.13q0.08-0.09 0.16-0.18zm0.61-0.46q-0.32 0.18-0.59 0.44 0.09-0.08 0.18-0.16 0.13-0.11 0.27-0.2 0.07-0.04 0.14-0.08zm-33.3-0.49q0.08-0.01 0.17-0.01l0.09 0.01q-0.05-0.01-0.09-0.01-0.09 0-0.17 0.01z"/>
|
||||
// <g id="Clip-Path" clipPath="url(#cp1)">
|
||||
// <g>
|
||||
// <g style={{opacity: .17}}>
|
||||
// <path className="s1" d="m36.14 2.62h-3.58l9.91-9.03 12.33 1.32 8.09 3.75-7.32 4.73c-1.1-0.2-3.42-0.48-3.91 0-0.6 0.61-0.6 2.26-0.77 3.52-0.13 1.02-1.08 0.94-1.54 0.77l-2.92 0.44-0.88 4.3c-0.24-0.17-0.87-0.85-1.49-2.26-0.77-1.76-1.76-2.31-3.35-2.48-1.28-0.13-3.93 0.79-4.57 1.11z"/>
|
||||
// </g>
|
||||
// <g>
|
||||
// <path className="s2" d="m36.14 2.62h-3.58l9.91-9.03 12.33 1.32 8.09 3.75-7.32 4.73c-1.1-0.2-3.42-0.48-3.91 0-0.6 0.61-0.6 2.26-0.77 3.52-0.13 1.02-1.08 0.94-1.54 0.77l-2.92 0.44-0.88 4.3c-0.24-0.17-0.87-0.85-1.49-2.26-0.77-1.76-1.76-2.31-3.35-2.48-1.28-0.13-3.93 0.79-4.57 1.11z"/>
|
||||
// </g>
|
||||
// </g>
|
||||
// </g>
|
||||
// <g>
|
||||
// <path fillRule="evenodd" className="s3" d="m46.78 5c-0.95-0.45-1.34-1.58-0.89-2.53 0.45-0.95 1.58-1.36 2.52-0.91 0.94 0.44 1.34 1.58 0.89 2.53-0.46 0.95-1.58 1.36-2.52 0.91z"/>
|
||||
// </g>
|
||||
// <g>
|
||||
// <path fillRule="evenodd" className="s4" d="m46.99 5.1c-0.83-0.39-1.18-1.37-0.79-2.2 0.4-0.82 1.38-1.17 2.2-0.78 0.83 0.39 1.18 1.37 0.79 2.2-0.39 0.82-1.38 1.17-2.2 0.78z"/>
|
||||
// </g>
|
||||
// <g>
|
||||
// <path fillRule="evenodd" className="s5" d="m47.99 1.98c0.9 0.17 1.5 1.03 1.33 1.93-0.16 0.9-1.02 1.49-1.92 1.32-0.9-0.16-1.49-1.02-1.33-1.92 0.17-0.9 1.03-1.49 1.92-1.33z"/>
|
||||
// </g>
|
||||
// <g>
|
||||
// <path fillRule="evenodd" className="s6" d="m48.05 2.04c0.9 0.16 1.49 1.02 1.32 1.92-0.16 0.9-1.02 1.49-1.92 1.33-0.9-0.17-1.49-1.03-1.33-1.93 0.17-0.89 1.03-1.49 1.93-1.32z"/>
|
||||
// </g>
|
||||
// </svg>
|
||||
// </button>
|
||||
// );
|
||||
// };
|
||||
|
||||
// Tag.displayName = "Tag";
|
||||
|
||||
// export default memo(Tag);
|
||||
|
||||
"use client";
|
||||
|
||||
import { memo } from "react";
|
||||
import { useRive, useStateMachineInput, Layout, Fit } from "@rive-app/react-canvas";
|
||||
import { useTagEffects } from "./useTagEffects";
|
||||
import { useState } from "react";
|
||||
|
||||
const STATE_MACHINE_NAME = "State Machine 1";
|
||||
const HOVER_INPUT_NAME = "Hover";
|
||||
interface TeamMember {
|
||||
id: string;
|
||||
name: string;
|
||||
role: string;
|
||||
image: string;
|
||||
social: {
|
||||
twitter?: string;
|
||||
linkedin?: string;
|
||||
github?: string;
|
||||
email?: string;
|
||||
};
|
||||
}
|
||||
|
||||
const Tag = () => {
|
||||
const { shouldShow, handleMouseEnter, handleClick } = useTagEffects();
|
||||
interface TagProps {
|
||||
members?: TeamMember[];
|
||||
}
|
||||
|
||||
const { rive, RiveComponent } = useRive({
|
||||
src: "/watermark-bob2.riv",
|
||||
stateMachines: STATE_MACHINE_NAME,
|
||||
autoplay: true,
|
||||
layout: new Layout({
|
||||
fit: Fit.Contain,
|
||||
}),
|
||||
});
|
||||
export default function Tag({ members = defaultMembers }: TagProps) {
|
||||
const [flipped, setFlipped] = useState<{ [key: string]: boolean }>({});
|
||||
|
||||
const hoverInput = useStateMachineInput(rive, STATE_MACHINE_NAME, HOVER_INPUT_NAME);
|
||||
const toggleFlip = (id: string) => {
|
||||
setFlipped((prev) => ({
|
||||
...prev,
|
||||
[id]: !prev[id],
|
||||
}));
|
||||
};
|
||||
|
||||
const handleTagClick = () => {
|
||||
window.open('https://webild.io', '_blank');
|
||||
};
|
||||
return (
|
||||
<div className="w-full py-12 px-4">
|
||||
<div className="max-w-6xl mx-auto">
|
||||
<h2 className="text-4xl font-bold text-center mb-12 text-gray-900">
|
||||
Our Team
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
{members.map((member) => (
|
||||
<div
|
||||
key={member.id}
|
||||
className="h-80 cursor-pointer perspective"
|
||||
onClick={() => toggleFlip(member.id)}
|
||||
>
|
||||
<div
|
||||
className="relative w-full h-full transition-transform duration-500 transform-gpu"
|
||||
style={{
|
||||
transformStyle: "preserve-3d",
|
||||
transform: flipped[member.id]
|
||||
? "rotateY(180deg)"
|
||||
: "rotateY(0deg)",
|
||||
}}
|
||||
>
|
||||
{/* Front Side */}
|
||||
<div
|
||||
className="absolute w-full h-full bg-white rounded-lg shadow-lg p-6 flex flex-col items-center justify-center"
|
||||
style={{
|
||||
backfaceVisibility: "hidden",
|
||||
}}
|
||||
>
|
||||
<div className="w-32 h-32 mb-4 rounded-full overflow-hidden bg-gray-200">
|
||||
<img
|
||||
src={member.image}
|
||||
alt={member.name}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
<h3 className="text-2xl font-bold text-gray-900 text-center">
|
||||
{member.name}
|
||||
</h3>
|
||||
<p className="text-gray-600 text-center mt-2">{member.role}</p>
|
||||
<p className="text-sm text-gray-400 mt-4">Hover to see links</p>
|
||||
</div>
|
||||
|
||||
const onMouseEnter = () => {
|
||||
handleMouseEnter();
|
||||
if (hoverInput) {
|
||||
hoverInput.value = true;
|
||||
}
|
||||
};
|
||||
{/* Back Side */}
|
||||
<div
|
||||
className="absolute w-full h-full bg-gradient-to-br from-blue-500 to-purple-600 rounded-lg shadow-lg p-6 flex flex-col items-center justify-center"
|
||||
style={{
|
||||
backfaceVisibility: "hidden",
|
||||
transform: "rotateY(180deg)",
|
||||
}}
|
||||
>
|
||||
<h3 className="text-2xl font-bold text-white mb-6 text-center">
|
||||
{member.name}
|
||||
</h3>
|
||||
<div className="flex flex-col gap-4 w-full">
|
||||
{member.social.twitter && (
|
||||
<a
|
||||
href={member.social.twitter}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center justify-center gap-2 bg-white bg-opacity-20 hover:bg-opacity-30 text-white py-2 px-4 rounded-lg transition-all duration-200"
|
||||
>
|
||||
<span>𝕏</span>
|
||||
<span>Twitter</span>
|
||||
</a>
|
||||
)}
|
||||
{member.social.linkedin && (
|
||||
<a
|
||||
href={member.social.linkedin}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center justify-center gap-2 bg-white bg-opacity-20 hover:bg-opacity-30 text-white py-2 px-4 rounded-lg transition-all duration-200"
|
||||
>
|
||||
<span>in</span>
|
||||
<span>LinkedIn</span>
|
||||
</a>
|
||||
)}
|
||||
{member.social.github && (
|
||||
<a
|
||||
href={member.social.github}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center justify-center gap-2 bg-white bg-opacity-20 hover:bg-opacity-30 text-white py-2 px-4 rounded-lg transition-all duration-200"
|
||||
>
|
||||
<span>⚙</span>
|
||||
<span>GitHub</span>
|
||||
</a>
|
||||
)}
|
||||
{member.social.email && (
|
||||
<a
|
||||
href={`mailto:${member.social.email}`}
|
||||
className="flex items-center justify-center gap-2 bg-white bg-opacity-20 hover:bg-opacity-30 text-white py-2 px-4 rounded-lg transition-all duration-200"
|
||||
>
|
||||
<span>✉</span>
|
||||
<span>Email</span>
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const onMouseLeave = () => {
|
||||
if (hoverInput) {
|
||||
hoverInput.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
if (!shouldShow) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
className="fixed z-[99999] bottom-6 right-6 w-[160px] h-[92px] cursor-pointer"
|
||||
onClick={(e) => handleClick(e, handleTagClick)}
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
>
|
||||
<RiveComponent className="w-full h-full" />
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
Tag.displayName = "Tag";
|
||||
|
||||
export default memo(Tag);
|
||||
const defaultMembers: TeamMember[] = [
|
||||
{
|
||||
id: "1",
|
||||
name: "Sarah Johnson",
|
||||
role: "Lead Designer",
|
||||
image:
|
||||
"https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=400&h=400&fit=crop",
|
||||
social: {
|
||||
twitter: "https://twitter.com",
|
||||
linkedin: "https://linkedin.com",
|
||||
email: "sarah@example.com",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
name: "Michael Chen",
|
||||
role: "Full Stack Developer",
|
||||
image:
|
||||
"https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=400&h=400&fit=crop",
|
||||
social: {
|
||||
github: "https://github.com",
|
||||
linkedin: "https://linkedin.com",
|
||||
email: "michael@example.com",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
name: "Emma Rodriguez",
|
||||
role: "Product Manager",
|
||||
image:
|
||||
"https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=400&h=400&fit=crop",
|
||||
social: {
|
||||
twitter: "https://twitter.com",
|
||||
linkedin: "https://linkedin.com",
|
||||
email: "emma@example.com",
|
||||
},
|
||||
},
|
||||
];
|
||||
Reference in New Issue
Block a user