Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,443 changes: 639 additions & 804 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
},
"dependencies": {
"@cloudflare/vite-plugin": "1.30.1",
"@customcads/react-sdk": "^1.13.5-beta.2",
"@customcads/react-sdk": "^1.13.5-beta.6",
"@icons-pack/react-simple-icons": "^13.11.1",
"@microsoft/signalr": "^10.0.0",
"@tanstack/react-form": "^1.32.1",
Expand All @@ -41,10 +41,11 @@
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
"embla-carousel-react": "^8.6.0",
"i18next": "^26.3.0",
"js-cookie": "^3.0.7",
"lucide-react": "^1.16.0",
"radix-ui": "^1.4.3",
"radix-ui": "^1.5.0",
"react": "^19.2.6",
"react-dom": "^19.2.6",
"react-i18next": "^17.0.8",
Expand Down
Binary file added public/home/popular/background.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/home/services/background.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/home/services/cart.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/home/services/delivery.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/home/services/download.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/home/services/market.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/home/services/order.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/home/services/print.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
File renamed without changes
20 changes: 11 additions & 9 deletions src/app/components/layout/footer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,17 @@ const Footer = () => {
return (
<footer className='flex flex-col bg-footer 2xl:bg-secondary text-footer-foreground rounded-t-sm transition-colors duration-400 z-10'>
<ul className='flex flex-col sm:flex-row justify-around items-center mx-5 py-10 gap-x-4 gap-y-10'>
<li className='order-3 flex justify-center items-center gap-x-6 animate-fade-in delay-800'>
<FooterLink to='.' text={tFooter('privacy-policy')} />
<FooterLink to='.' text={tFooter('terms-conditions')} />
<li className='flex justify-center items-center gap-x-6 animate-fade-in delay-800'>
<FooterLink
to='/privacy-policy'
text={tFooter('privacy-policy')}
/>
<FooterLink
to='/terms-of-service'
text={tFooter('terms-conditions')}
/>
</li>
<li className='order-1 sm:order-2 flex justify-center gap-4 animate-fade-in delay-600'>
<li className='flex justify-center gap-4 animate-fade-in delay-600'>
<FooterIcon
link='https://www.instagram.com/custom_cads'
Icon={SiInstagram}
Expand All @@ -33,14 +39,10 @@ const Footer = () => {
Icon={SiFacebook}
/>
<FooterIcon
link='https://github.com/NinjataWRLD/CustomCADs-Frontend/'
link='https://github.com/CustomCADs/'
Icon={SiGithub}
/>
</li>
<li className='order-2 sm:order-1 flex justify-center items-center gap-x-4 animate-fade-in delay-800'>
<FooterLink to='.' text={tFooter('about-us')} />
<FooterLink to='.' text={tFooter('customer-support')} />
</li>
</ul>
<div className='flex justify-center items-center bg-footer-accent text-footer-accent-foreground py-2 rounded-t-lg transition-colors duration-400'>
<FooterHeading />
Expand Down
16 changes: 7 additions & 9 deletions src/app/components/layout/footer/link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@ import { Link, LinkProps } from '@tanstack/react-router';

type Props = LinkProps & { text: string };

const FooterLink = ({ text, ...props }: Props) => {
return (
<span className='text-footer-foreground text-sm md:text-base font-bold underline hover:text-footer-accent-foreground'>
<Link {...props} className='ease-in duration-100'>
{text}
</Link>
</span>
);
};
const FooterLink = ({ text, ...props }: Props) => (
<span className='text-footer-foreground text-sm md:text-base font-bold underline hover:text-footer-accent-foreground'>
<Link {...props} className='ease-in duration-100'>
{text}
</Link>
</span>
);

export default FooterLink;
248 changes: 248 additions & 0 deletions src/app/components/ui/carousel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
/* eslint-disable max-lines */
/* eslint-disable i18next/no-literal-string */
import {
createContext,
useCallback,
useContext,
useEffect,
useState,
} from 'react';
import useEmblaCarousel, { UseEmblaCarouselType } from 'embla-carousel-react';
import { ArrowLeft, ArrowRight } from 'lucide-react';
import { cn } from '@/lib/utils';
import { Button } from './';

type CarouselApi = UseEmblaCarouselType[1];
type UseCarouselParameters = Parameters<typeof useEmblaCarousel>;
type CarouselOptions = UseCarouselParameters[0];
type CarouselPlugin = UseCarouselParameters[1];

type CarouselProps = {
opts?: CarouselOptions;
plugins?: CarouselPlugin;
orientation?: 'horizontal' | 'vertical';
setApi?: (api: CarouselApi) => void;
};

type CarouselContextProps = {
carouselRef: ReturnType<typeof useEmblaCarousel>[0];
api: ReturnType<typeof useEmblaCarousel>[1];
scrollPrev: () => void;
scrollNext: () => void;
canScrollPrev: boolean;
canScrollNext: boolean;
} & CarouselProps;

const CarouselContext = createContext<CarouselContextProps | null>(null);

const useCarousel = () => {
const context = useContext(CarouselContext);

if (!context) {
throw new Error('useCarousel must be used within a <Carousel />');
}

return context;
};

const Carousel = ({
orientation = 'horizontal',
opts,
setApi,
plugins,
className,
children,
...props
}: React.ComponentProps<'div'> & CarouselProps) => {
const [carouselRef, api] = useEmblaCarousel(
{
...opts,
axis: orientation === 'horizontal' ? 'x' : 'y',
},
plugins,
);
const [canScrollPrev, setCanScrollPrev] = useState(false);
const [canScrollNext, setCanScrollNext] = useState(false);

const onSelect = useCallback((api: CarouselApi) => {
if (!api) return;
setCanScrollPrev(api.canScrollPrev());
setCanScrollNext(api.canScrollNext());
}, []);

const scrollPrev = useCallback(() => {
api?.scrollPrev();
}, [api]);

const scrollNext = useCallback(() => {
api?.scrollNext();
}, [api]);

const handleKeyDown = useCallback(
(event: React.KeyboardEvent<HTMLDivElement>) => {
if (event.key === 'ArrowLeft') {
event.preventDefault();
scrollPrev();
} else if (event.key === 'ArrowRight') {
event.preventDefault();
scrollNext();
}
},
[scrollPrev, scrollNext],
);

useEffect(() => {
if (!api || !setApi) return;
setApi(api);
}, [api, setApi]);

useEffect(() => {
if (!api) return;
onSelect(api);
api.on('reInit', onSelect);
api.on('select', onSelect);

return () => {
api?.off('select', onSelect);
};
}, [api, onSelect]);

return (
<CarouselContext.Provider
value={{
carouselRef,
api: api,
opts,
orientation:
orientation ||
(opts?.axis === 'y' ? 'vertical' : 'horizontal'),
scrollPrev,
scrollNext,
canScrollPrev,
canScrollNext,
}}
>
<div
onKeyDownCapture={handleKeyDown}
className={cn('relative', className)}
role='region'
aria-roledescription='carousel'
data-slot='carousel'
{...props}
>
{children}
</div>
</CarouselContext.Provider>
);
};

const CarouselContent = ({
className,
...props
}: React.ComponentProps<'div'>) => {
const { carouselRef, orientation } = useCarousel();

return (
<div
ref={carouselRef}
className='overflow-hidden'
data-slot='carousel-content'
>
<div
className={cn(
'flex',
orientation === 'horizontal' ? '-ml-4' : '-mt-4 flex-col',
className,
)}
{...props}
/>
</div>
);
};

const CarouselItem = ({ className, ...props }: React.ComponentProps<'div'>) => {
const { orientation } = useCarousel();

return (
<div
role='group'
aria-roledescription='slide'
data-slot='carousel-item'
className={cn(
'min-w-0 shrink-0 grow-0 basis-full',
orientation === 'horizontal' ? 'pl-4' : 'pt-4',
className,
)}
{...props}
/>
);
};

const CarouselPrevious = ({
className,
variant = 'outline',
size = 'icon',
...props
}: React.ComponentProps<typeof Button>) => {
const { orientation, scrollPrev, canScrollPrev } = useCarousel();

return (
<Button
data-slot='carousel-previous'
variant={variant}
size={size}
className={cn(
'absolute size-8 rounded-full',
orientation === 'horizontal'
? 'top-1/2 -left-12 -translate-y-1/2'
: '-top-12 left-1/2 -translate-x-1/2 rotate-90',
className,
)}
disabled={!canScrollPrev}
onClick={scrollPrev}
{...props}
>
<ArrowLeft />
<span className='sr-only'>Previous slide</span>
</Button>
);
};

const CarouselNext = ({
className,
variant = 'outline',
size = 'icon',
...props
}: React.ComponentProps<typeof Button>) => {
const { orientation, scrollNext, canScrollNext } = useCarousel();

return (
<Button
data-slot='carousel-next'
variant={variant}
size={size}
className={cn(
'absolute size-8 rounded-full',
orientation === 'horizontal'
? 'top-1/2 -right-12 -translate-y-1/2'
: '-bottom-12 left-1/2 -translate-x-1/2 rotate-90',
className,
)}
disabled={!canScrollNext}
onClick={scrollNext}
{...props}
>
<ArrowRight />
<span className='sr-only'>Next slide</span>
</Button>
);
};

export {
type CarouselApi as Api,
Carousel as Root,
CarouselContent as Content,
CarouselItem as Item,
CarouselPrevious as Previous,
CarouselNext as Next,
};
36 changes: 36 additions & 0 deletions src/app/components/ui/hover-card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { HoverCard as HoverCardPrimitive } from 'radix-ui';
import { cn } from '@/lib/utils';

const HoverCard = ({
...props
}: React.ComponentProps<typeof HoverCardPrimitive.Root>) => (
<HoverCardPrimitive.Root data-slot='hover-card' {...props} />
);

const HoverCardTrigger = ({
...props
}: React.ComponentProps<typeof HoverCardPrimitive.Trigger>) => (
<HoverCardPrimitive.Trigger data-slot='hover-card-trigger' {...props} />
);

const HoverCardContent = ({
className,
align = 'center',
sideOffset = 4,
...props
}: React.ComponentProps<typeof HoverCardPrimitive.Content>) => (
<HoverCardPrimitive.Portal data-slot='hover-card-portal'>
<HoverCardPrimitive.Content
data-slot='hover-card-content'
align={align}
sideOffset={sideOffset}
className={cn(
'z-50 w-64 origin-(--radix-hover-card-content-transform-origin) rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-hidden data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',
className,
)}
{...props}
/>
</HoverCardPrimitive.Portal>
);

export { HoverCard, HoverCardTrigger, HoverCardContent };
1 change: 1 addition & 0 deletions src/app/components/ui/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export * as alert from './alert';
export * as alertDialog from './alert-dialog';
export { Button } from './button';
export * as card from './card';
export * as carousel from './carousel';
export { Checkbox } from './checkbox';
export * as collapsible from './collapsible';
export * as command from './command';
Expand Down
4 changes: 4 additions & 0 deletions src/app/constants/global.ts
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
export const TITLE = 'CustomCADs';

export const TAGS = {
POPULAR: 'Popular',
};
1 change: 1 addition & 0 deletions src/app/constants/limits.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export const POPULAR = { default: 12 };
export const GALLERY = { default: 12, min: 1, max: 50 };
2 changes: 1 addition & 1 deletion src/app/hooks/locales/translations/pages/public.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useTranslation } from '../../useTranslation';

type Info = 'home' | 'preferences';
type Info = 'home' | 'preferences' | 'services';
export const useInfoTranslations = <N extends Info>(ns: N) =>
useTranslation(`pages.public.info.${ns}`).t;

Expand Down
2 changes: 2 additions & 0 deletions src/app/locales/bg-BG/pages/public/info/home.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ export default {
'Било то пазаруване или поръчване, и от приноси до продажби, предлагаме услугите си на всички заинтересовани!',
section1_buy: 'Купи Продукт',
section1_sell: 'Продай Продукт',
section2_title: 'Услугите, които предлагаме:',
section3_title: 'Най-популярните ни Продукти:',
} satisfies Home;
Loading
Loading