mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2026-03-13 02:01:57 +08:00
Compare commits
1 Commits
devops-con
...
feat/ratin
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dd71900f8b |
146
src/components/Rating/Rating.tsx
Normal file
146
src/components/Rating/Rating.tsx
Normal file
@@ -0,0 +1,146 @@
|
||||
import { useState } from 'react';
|
||||
import { cn } from '../../lib/classname';
|
||||
|
||||
type StarValue = 0 | 0.5 | 1;
|
||||
|
||||
type RatingProps = {
|
||||
ratings?: number;
|
||||
size?: number;
|
||||
};
|
||||
|
||||
export function Rating(props: RatingProps) {
|
||||
const { ratings = 0, size } = props;
|
||||
|
||||
const [stars, setStars] = useState(
|
||||
Array.from({ length: 5 }, (_, i) => {
|
||||
const rating = Math.floor(ratings);
|
||||
if (i < rating) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (i === rating && ratings % 1 !== 0) {
|
||||
return 0.5;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}),
|
||||
);
|
||||
|
||||
const [dynamicStars, setDynamicStars] = useState(stars);
|
||||
|
||||
return (
|
||||
<div className="mt-4 flex">
|
||||
{dynamicStars.map((star, counter) => (
|
||||
<RatingStar
|
||||
key={`star-${counter}`}
|
||||
value={star}
|
||||
onValueChange={(value) => {
|
||||
const newStars = [...stars];
|
||||
newStars.fill(1, 0, counter);
|
||||
newStars[counter] = value;
|
||||
newStars.fill(0, counter + 1);
|
||||
|
||||
setDynamicStars(newStars);
|
||||
}}
|
||||
onClick={(value) => {
|
||||
const newStars = [...stars];
|
||||
newStars.fill(1, 0, counter);
|
||||
newStars[counter] = value;
|
||||
newStars.fill(0, counter + 1);
|
||||
|
||||
setStars(newStars);
|
||||
setDynamicStars(newStars);
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
setDynamicStars(stars);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type RatingStarProps = {
|
||||
value: StarValue;
|
||||
onValueChange?: (value: StarValue) => void;
|
||||
onMouseLeave?: () => void;
|
||||
onClick: (value: StarValue) => void;
|
||||
startSize?: number;
|
||||
};
|
||||
|
||||
export function RatingStar(props: RatingStarProps) {
|
||||
const { value, onValueChange, onClick, onMouseLeave, startSize = 20 } = props;
|
||||
|
||||
return (
|
||||
<span
|
||||
onMouseMove={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const rect = e.currentTarget.getBoundingClientRect();
|
||||
const mid = rect.width / 2;
|
||||
const value = e.clientX < rect.left + mid ? 0.5 : 1;
|
||||
onValueChange?.(value);
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const rect = e.currentTarget.getBoundingClientRect();
|
||||
const mid = rect.width / 2;
|
||||
const value = e.clientX < rect.left + mid ? 0.5 : 1;
|
||||
onValueChange?.(value);
|
||||
}}
|
||||
onMouseLeave={onMouseLeave}
|
||||
onClick={() => {
|
||||
onClick(value);
|
||||
}}
|
||||
className="relative block cursor-pointer"
|
||||
style={{
|
||||
width: `${startSize}px`,
|
||||
height: `${startSize}px`,
|
||||
}}
|
||||
>
|
||||
<span
|
||||
className={cn(
|
||||
'absolute overflow-hidden',
|
||||
value === 0.5 ? 'w-1/2' : 'w-0',
|
||||
)}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className="fill-red"
|
||||
style={{
|
||||
width: `${startSize}px`,
|
||||
height: `${startSize}px`,
|
||||
}}
|
||||
>
|
||||
<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" />
|
||||
</svg>
|
||||
</span>
|
||||
|
||||
<span className="absolute">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className={cn('', value === 1 ? 'fill-red' : 'fill-none')}
|
||||
style={{
|
||||
width: `${startSize}px`,
|
||||
height: `${startSize}px`,
|
||||
}}
|
||||
>
|
||||
<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" />
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import { useAuth } from '../hooks/use-auth';
|
||||
import { pageProgressMessage } from '../stores/page';
|
||||
import { useToast } from '../hooks/use-toast';
|
||||
import { type UserTeamItem } from './TeamDropdown/TeamDropdown';
|
||||
import { Rating } from './Rating/Rating';
|
||||
|
||||
export function TeamsList() {
|
||||
const [teamList, setTeamList] = useState<UserTeamItem[]>([]);
|
||||
@@ -11,7 +12,7 @@ export function TeamsList() {
|
||||
const toast = useToast();
|
||||
async function getAllTeam() {
|
||||
const { response, error } = await httpGet<UserTeamItem[]>(
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-get-user-teams`
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-get-user-teams`,
|
||||
);
|
||||
if (error || !response) {
|
||||
toast.error(error?.message || 'Something went wrong');
|
||||
@@ -35,6 +36,17 @@ export function TeamsList() {
|
||||
<p className="mt-2 text-gray-400">
|
||||
Here are the teams you are part of
|
||||
</p>
|
||||
|
||||
<Rating ratings={0.5} />
|
||||
<Rating ratings={1} />
|
||||
<Rating ratings={1.5} />
|
||||
<Rating ratings={2} />
|
||||
<Rating ratings={2.5} />
|
||||
<Rating ratings={3} />
|
||||
<Rating ratings={3.5} />
|
||||
<Rating ratings={4} />
|
||||
<Rating ratings={4.5} />
|
||||
<Rating ratings={5} />
|
||||
</div>
|
||||
<ul className="mb-3 flex flex-col gap-1">
|
||||
<li>
|
||||
|
||||
Reference in New Issue
Block a user