add: rating page
This commit is contained in:
Generated
+81
@@ -10,6 +10,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@radix-ui/react-dialog": "^1.1.14",
|
"@radix-ui/react-dialog": "^1.1.14",
|
||||||
"@radix-ui/react-popover": "^1.1.14",
|
"@radix-ui/react-popover": "^1.1.14",
|
||||||
|
"@radix-ui/react-slider": "^1.3.5",
|
||||||
"@radix-ui/react-slot": "^1.2.3",
|
"@radix-ui/react-slot": "^1.2.3",
|
||||||
"@radix-ui/react-switch": "^1.2.5",
|
"@radix-ui/react-switch": "^1.2.5",
|
||||||
"@tanstack/react-query": "^5.81.5",
|
"@tanstack/react-query": "^5.81.5",
|
||||||
@@ -1031,6 +1032,12 @@
|
|||||||
"node": ">=12.4.0"
|
"node": ">=12.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/number": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/primitive": {
|
"node_modules/@radix-ui/primitive": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz",
|
||||||
@@ -1060,6 +1067,32 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-collection": {
|
||||||
|
"version": "1.1.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz",
|
||||||
|
"integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.2",
|
||||||
|
"@radix-ui/react-context": "1.1.2",
|
||||||
|
"@radix-ui/react-primitive": "2.1.3",
|
||||||
|
"@radix-ui/react-slot": "1.2.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-compose-refs": {
|
"node_modules/@radix-ui/react-compose-refs": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
|
||||||
@@ -1126,6 +1159,21 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-direction": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-dismissable-layer": {
|
"node_modules/@radix-ui/react-dismissable-layer": {
|
||||||
"version": "1.1.10",
|
"version": "1.1.10",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.10.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.10.tgz",
|
||||||
@@ -1351,6 +1399,39 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-slider": {
|
||||||
|
"version": "1.3.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-slider/-/react-slider-1.3.5.tgz",
|
||||||
|
"integrity": "sha512-rkfe2pU2NBAYfGaxa3Mqosi7VZEWX5CxKaanRv0vZd4Zhl9fvQrg0VM93dv3xGLGfrHuoTRF3JXH8nb9g+B3fw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/number": "1.1.1",
|
||||||
|
"@radix-ui/primitive": "1.1.2",
|
||||||
|
"@radix-ui/react-collection": "1.1.7",
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.2",
|
||||||
|
"@radix-ui/react-context": "1.1.2",
|
||||||
|
"@radix-ui/react-direction": "1.1.1",
|
||||||
|
"@radix-ui/react-primitive": "2.1.3",
|
||||||
|
"@radix-ui/react-use-controllable-state": "1.2.2",
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.1.1",
|
||||||
|
"@radix-ui/react-use-previous": "1.1.1",
|
||||||
|
"@radix-ui/react-use-size": "1.1.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-slot": {
|
"node_modules/@radix-ui/react-slot": {
|
||||||
"version": "1.2.3",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@radix-ui/react-dialog": "^1.1.14",
|
"@radix-ui/react-dialog": "^1.1.14",
|
||||||
"@radix-ui/react-popover": "^1.1.14",
|
"@radix-ui/react-popover": "^1.1.14",
|
||||||
|
"@radix-ui/react-slider": "^1.3.5",
|
||||||
"@radix-ui/react-slot": "^1.2.3",
|
"@radix-ui/react-slot": "^1.2.3",
|
||||||
"@radix-ui/react-switch": "^1.2.5",
|
"@radix-ui/react-switch": "^1.2.5",
|
||||||
"@tanstack/react-query": "^5.81.5",
|
"@tanstack/react-query": "^5.81.5",
|
||||||
|
|||||||
@@ -0,0 +1,130 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import ToggleButton from '@/components/button/ToggleButton';
|
||||||
|
import TabContainer, { TabContainerClassNames, TabContainerRenderType } from '@/components/tab/TabContainer';
|
||||||
|
import { TabHeader } from '@/components/tab/TabHeader';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import RateSelectionBar from '@/components/utils/RateSelectionBar';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
import { useParams } from 'next/navigation';
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
type Props = object
|
||||||
|
|
||||||
|
type Params = {
|
||||||
|
orderId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function RatingOrderIndex({ }: Props) {
|
||||||
|
const params: Params = useParams();
|
||||||
|
console.log(params)
|
||||||
|
|
||||||
|
const badPoitns = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="grid grid-cols-2 gap-x-4.5 gap-y-4 mt-8">
|
||||||
|
<ToggleButton name='minus1'>
|
||||||
|
اماده سازی زمانبر
|
||||||
|
</ToggleButton>
|
||||||
|
<ToggleButton name='minus2'>
|
||||||
|
بهداشت محیطی بد
|
||||||
|
</ToggleButton>
|
||||||
|
<ToggleButton name='minus3'>
|
||||||
|
کیفیت منو پایین
|
||||||
|
</ToggleButton>
|
||||||
|
<ToggleButton name='minus4'>
|
||||||
|
تنوع پایین منو
|
||||||
|
</ToggleButton>
|
||||||
|
<ToggleButton name='minus5'>
|
||||||
|
برخورد نامناسب کارکنان
|
||||||
|
</ToggleButton>
|
||||||
|
<ToggleButton name='minus6'>
|
||||||
|
قیمت نامناسب
|
||||||
|
</ToggleButton>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const goodPoitns = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-2 gap-x-4.5 gap-y-4 mt-8">
|
||||||
|
<ToggleButton name='plus1'>
|
||||||
|
اماده سازی سریع
|
||||||
|
</ToggleButton>
|
||||||
|
<ToggleButton name='plus2'>
|
||||||
|
بهداشت محیطی خوب
|
||||||
|
</ToggleButton>
|
||||||
|
<ToggleButton name='plus3'>
|
||||||
|
کیفیت منو خوب
|
||||||
|
</ToggleButton>
|
||||||
|
<ToggleButton name='plus4'>
|
||||||
|
تنوع بالا منو
|
||||||
|
</ToggleButton>
|
||||||
|
<ToggleButton name='plus5'>
|
||||||
|
برخورد مناسب کارکنان
|
||||||
|
</ToggleButton>
|
||||||
|
<ToggleButton name='plus6'>
|
||||||
|
قیمت مناسب
|
||||||
|
</ToggleButton>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const submitForm = (formData: FormData) => {
|
||||||
|
console.log(formData.get('rating'))
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className='min-h-full'>
|
||||||
|
<h1 className='font-medium'>امتیاز شما به رستوران</h1>
|
||||||
|
|
||||||
|
<form action={submitForm} className='bg-container h-full flex flex-col justify-between rounded-[30px] pt-10 px-6 pb-7.5 mt-4'>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div className='w-full'>
|
||||||
|
<RateSelectionBar name='rating'/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='mt-10'>
|
||||||
|
<TabContainer
|
||||||
|
defaultIndex={1}
|
||||||
|
changeType={TabContainerRenderType.VISIBILITY}
|
||||||
|
className={{
|
||||||
|
...TabContainerClassNames,
|
||||||
|
wrapper: 'p-2!',
|
||||||
|
scrollView: clsx(
|
||||||
|
'border-none! rounded-xl! gap-0! h-full bg-[#EAECF0]! grid! grid-cols-2 px-2! py-2! pt-2!', TabContainerClassNames.scrollView
|
||||||
|
),
|
||||||
|
title: 'text-sm2! font-normal mt-1',
|
||||||
|
header: 'rounded-lg h-7! w-full',
|
||||||
|
headerActive: 'bg-primary',
|
||||||
|
titleActive: 'text-accent'
|
||||||
|
}}>
|
||||||
|
<TabHeader title='نقاط ضعف' viewRenderer={badPoitns()}></TabHeader>
|
||||||
|
<TabHeader title='نقاط قوت' viewRenderer={goodPoitns()}></TabHeader>
|
||||||
|
</TabContainer>
|
||||||
|
<div className="w-full mt-6">
|
||||||
|
<label htmlFor='userOpinion'>نظر شما</label>
|
||||||
|
<br />
|
||||||
|
<textarea name='userOpinion' id='userOpinion' placeholder='نظر شما' className='mt-2 w-full h-21 px-4 py-2.5 text-xs text-[#888888] rounded-xl border border-[#D0D0D0]' />
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-2 gap-4 mt-6">
|
||||||
|
<Button type='submit' className='mt-auto rounded-xl font-normal cursor-pointer'>ثبت بازخورد</Button>
|
||||||
|
<Button type='submit' className='mt-auto rounded-xl bg-white cursor-pointer hover:bg-neutral-100 border border-foreground text-foreground font-normal'>بازگشت</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RatingOrderIndex
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
import clsx from 'clsx';
|
||||||
|
import { motion } from 'framer-motion';
|
||||||
|
import { Refresh } from 'iconsax-react';
|
||||||
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
className?: ToggleButtonClassName;
|
||||||
|
pending?: boolean;
|
||||||
|
defaultChecked?: boolean;
|
||||||
|
onToggle?: (checked: boolean) => void;
|
||||||
|
name: string; // required for form data
|
||||||
|
children?: React.ReactNode;
|
||||||
|
disabled?: boolean;
|
||||||
|
} & Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'className' | 'onClick' | 'type'>;
|
||||||
|
|
||||||
|
export type ToggleButtonClassName = {
|
||||||
|
wrapper?: string;
|
||||||
|
wrapperDeactive?: string;
|
||||||
|
wrapperActive?: string;
|
||||||
|
wrapperDisabled?: string;
|
||||||
|
wrapperEnabled?: string;
|
||||||
|
wrapperPending?: string;
|
||||||
|
icon?: string;
|
||||||
|
iconDeactive?: string;
|
||||||
|
iconActive?: string;
|
||||||
|
content?: string;
|
||||||
|
contentDeactive?: string;
|
||||||
|
contentActive?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ToggleButtonClassNames: ToggleButtonClassName = {
|
||||||
|
wrapper:
|
||||||
|
'relative transition-all border border-solid duration-200 cursor-pointer w-full rounded-normal p-3 font-normal text-sm overflow-clip',
|
||||||
|
wrapperDeactive: 'bg-white border-neutral-200',
|
||||||
|
wrapperActive: 'border-[#439C46]',
|
||||||
|
wrapperDisabled: 'bg-disabled text-disabled-text',
|
||||||
|
wrapperEnabled: 'bg-container text-white hover:bg-gray-100 active:brightness-110',
|
||||||
|
wrapperPending: 'bg-transparent text-transparent cursor-auto',
|
||||||
|
content: 'text-xs transition-all duration-150',
|
||||||
|
contentDeactive: 'text-foreground',
|
||||||
|
contentActive: 'text-[#439C46]',
|
||||||
|
};
|
||||||
|
|
||||||
|
function ToggleButton({
|
||||||
|
className = ToggleButtonClassNames,
|
||||||
|
disabled = false,
|
||||||
|
pending = false,
|
||||||
|
defaultChecked = false,
|
||||||
|
onToggle,
|
||||||
|
name,
|
||||||
|
children,
|
||||||
|
...rest
|
||||||
|
}: Props) {
|
||||||
|
const [checked, setChecked] = useState(defaultChecked);
|
||||||
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
if (disabled || pending) return;
|
||||||
|
const newChecked = !checked;
|
||||||
|
setChecked(newChecked);
|
||||||
|
onToggle?.(newChecked);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const form = inputRef.current?.form;
|
||||||
|
if (!form) return;
|
||||||
|
|
||||||
|
const onReset = () => setChecked(defaultChecked);
|
||||||
|
form.addEventListener('reset', onReset);
|
||||||
|
return () => form.removeEventListener('reset', onReset);
|
||||||
|
}, [defaultChecked]);
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
name={name}
|
||||||
|
checked={checked}
|
||||||
|
readOnly
|
||||||
|
hidden
|
||||||
|
ref={inputRef}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
disabled={disabled}
|
||||||
|
onClick={handleClick}
|
||||||
|
className={clsx(
|
||||||
|
className.wrapper,
|
||||||
|
disabled ? className.wrapperDisabled : className.wrapperEnabled,
|
||||||
|
pending && className.wrapperPending,
|
||||||
|
checked ? className.wrapperActive : className.wrapperDeactive
|
||||||
|
)}
|
||||||
|
{...rest}
|
||||||
|
>
|
||||||
|
{pending && (
|
||||||
|
<motion.div
|
||||||
|
className="absolute left-0 top-0 h-full w-full bg-neutral-200"
|
||||||
|
initial={{ y: '100%' }}
|
||||||
|
animate={{ y: 0 }}
|
||||||
|
>
|
||||||
|
<motion.div
|
||||||
|
animate={{ rotateZ: [0, 360] }}
|
||||||
|
transition={{ duration: 1.2, repeat: Infinity, ease: 'linear' }}
|
||||||
|
whileHover={{ scale: 1.1 }}
|
||||||
|
whileTap={{ scale: 0.9 }}
|
||||||
|
className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2"
|
||||||
|
>
|
||||||
|
<Refresh stroke="#333333" size={24} />
|
||||||
|
</motion.div>
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<span
|
||||||
|
className={clsx(
|
||||||
|
className.content,
|
||||||
|
disabled || !checked ? className.contentDeactive : className.contentActive
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ToggleButton;
|
||||||
@@ -11,8 +11,15 @@ type Props = {
|
|||||||
itemRenderer?: (child: ReactNode, index: number) => ReactElement;
|
itemRenderer?: (child: ReactNode, index: number) => ReactElement;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
className?: TabContainerClassName;
|
className?: TabContainerClassName;
|
||||||
|
changeType?: TabContainerRenderType;
|
||||||
|
defaultIndex?: number;
|
||||||
} & Omit<React.OlHTMLAttributes<HTMLUListElement>, 'className'>;
|
} & Omit<React.OlHTMLAttributes<HTMLUListElement>, 'className'>;
|
||||||
|
|
||||||
|
export enum TabContainerRenderType {
|
||||||
|
RENDERER,
|
||||||
|
VISIBILITY,
|
||||||
|
}
|
||||||
|
|
||||||
export type TabContainerClassName = {
|
export type TabContainerClassName = {
|
||||||
wrapper?: string;
|
wrapper?: string;
|
||||||
scrollView?: string;
|
scrollView?: string;
|
||||||
@@ -41,9 +48,9 @@ export const TabContainerClassNames: TabContainerClassName = {
|
|||||||
titleActive: 'text-black'
|
titleActive: 'text-black'
|
||||||
}
|
}
|
||||||
|
|
||||||
function TabContainer({ className = TabContainerClassNames, itemRenderer, children, ...restProps }: Props) {
|
function TabContainer({ className = TabContainerClassNames, changeType = TabContainerRenderType.RENDERER, defaultIndex = 0, itemRenderer, children, ...restProps }: Props) {
|
||||||
|
|
||||||
const [selectedTab, setSelectedTab] = useState(0);
|
const [selectedTab, setSelectedTab] = useState(defaultIndex);
|
||||||
|
|
||||||
const updateTab = useCallback((index: number) => {
|
const updateTab = useCallback((index: number) => {
|
||||||
setSelectedTab(index);
|
setSelectedTab(index);
|
||||||
@@ -99,9 +106,24 @@ function TabContainer({ className = TabContainerClassNames, itemRenderer, childr
|
|||||||
})}
|
})}
|
||||||
</HorizontalScrollView>
|
</HorizontalScrollView>
|
||||||
|
|
||||||
<div>
|
{changeType === TabContainerRenderType.RENDERER &&
|
||||||
{items[selectedTab]?.props?.viewRenderer ?? <div></div>}
|
<div>
|
||||||
</div>
|
{items[selectedTab]?.props?.viewRenderer ?? <div></div>}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
{changeType === TabContainerRenderType.VISIBILITY &&
|
||||||
|
<>
|
||||||
|
{items.map((item, index) => {
|
||||||
|
return (
|
||||||
|
<div key={index} className={clsx(
|
||||||
|
index === selectedTab ? 'visible' : 'hidden'
|
||||||
|
)}>
|
||||||
|
{item.props.viewRenderer}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import * as SliderPrimitive from "@radix-ui/react-slider"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
function Slider({
|
||||||
|
className,
|
||||||
|
defaultValue,
|
||||||
|
value,
|
||||||
|
min = 0,
|
||||||
|
max = 100,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof SliderPrimitive.Root>) {
|
||||||
|
const _values = React.useMemo(
|
||||||
|
() =>
|
||||||
|
Array.isArray(value)
|
||||||
|
? value
|
||||||
|
: Array.isArray(defaultValue)
|
||||||
|
? defaultValue
|
||||||
|
: [min, max],
|
||||||
|
[value, defaultValue, min, max]
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SliderPrimitive.Root
|
||||||
|
data-slot="slider"
|
||||||
|
defaultValue={defaultValue}
|
||||||
|
value={value}
|
||||||
|
min={min}
|
||||||
|
max={max}
|
||||||
|
className={cn(
|
||||||
|
"relative flex w-full touch-none items-center select-none data-[disabled]:opacity-50 data-[orientation=vertical]:h-full data-[orientation=vertical]:min-h-44 data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<SliderPrimitive.Track
|
||||||
|
data-slot="slider-track"
|
||||||
|
className={cn(
|
||||||
|
"bg-muted relative grow overflow-hidden rounded-full data-[orientation=horizontal]:h-1.5 data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-1.5"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<SliderPrimitive.Range
|
||||||
|
data-slot="slider-range"
|
||||||
|
className={cn(
|
||||||
|
"bg-primary absolute data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</SliderPrimitive.Track>
|
||||||
|
{Array.from({ length: _values.length }, (_, index) => (
|
||||||
|
<SliderPrimitive.Thumb
|
||||||
|
data-slot="slider-thumb"
|
||||||
|
key={index}
|
||||||
|
className="border-primary bg-background ring-ring/50 block size-3 shrink-0 rounded-full border shadow-sm transition-[color,box-shadow] hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50"
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</SliderPrimitive.Root>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Slider }
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import { Star, Star1 } from 'iconsax-react'
|
||||||
|
import React from 'react'
|
||||||
|
import { Slider } from '../ui/slider';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
content?: Array<{ value: string, icon: React.ReactElement }>;
|
||||||
|
className?: string;
|
||||||
|
name?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultContent: Array<{ value: string, icon: React.ReactElement }> = [
|
||||||
|
{ value: '5', icon: <Star className='fill-black me-1 mb-1' size={24} /> },
|
||||||
|
{ value: '4', icon: <Star className='fill-black me-1 mb-1' size={24} /> },
|
||||||
|
{ value: '3', icon: <Star className='fill-black me-1 mb-1' size={24} /> },
|
||||||
|
{ value: '2', icon: <Star className='fill-black me-1 mb-1' size={24} /> },
|
||||||
|
{ value: '1', icon: <Star className='fill-black me-1 mb-1' size={24} /> },
|
||||||
|
]
|
||||||
|
|
||||||
|
function RateSelectionBar({ name, content = defaultContent, className = '' }: Props) {
|
||||||
|
return (
|
||||||
|
<div className={`${className}`}>
|
||||||
|
<div className='relative w-full h-3 rounded-full'>
|
||||||
|
<Slider
|
||||||
|
min={1}
|
||||||
|
max={content.length}
|
||||||
|
name={name}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between items-center mt-2">
|
||||||
|
{content?.map((value, index) => {
|
||||||
|
return (
|
||||||
|
<span key={index} className='flex items-center gap-1'>
|
||||||
|
<Star1 className='fill-black mb-1' size={12} />
|
||||||
|
<span className='text-xs font-medium'>{value.value}</span>
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RateSelectionBar
|
||||||
Reference in New Issue
Block a user