Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
salamca committed Oct 20, 2023
2 parents 9e6b0d1 + a0a1ea2 commit f801909
Show file tree
Hide file tree
Showing 10 changed files with 279 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import IconStarFull from "../../../../../../../components/ui/icons/star-full";
import Link from "../../../../../../../components/ui/link";
import { CragRoutesContext } from "../../crag-routes";
import { pluralizeNoun } from "../../../../../../../utils/text-helpers";
import RouteGrade from "./crag-route/route-grade";

interface Props {
crag: Crag;
Expand Down Expand Up @@ -46,7 +47,7 @@ function CragRoute({ crag, route, ascent }: Props) {
{/* Route difficulty */}
{displayColumn("difficulty") && (
<td className="p-4">
<RouteGrade route={route} />
<RouteGrade route={route} crag={crag} />
</td>
)}

Expand Down Expand Up @@ -133,7 +134,7 @@ function CragRouteCompact({ crag, route, ascent }: Props) {
<div>
{displayColumn("difficulty") && (
<span className="pr-4">
<RouteGrade route={route} />
<RouteGrade route={route} crag={crag} />
</span>
)}
{displayColumn("length") && route.length && (
Expand Down Expand Up @@ -164,19 +165,6 @@ function CragRouteCompact({ crag, route, ascent }: Props) {
);
}

interface RouteGradeProps {
route: Route;
}

function RouteGrade({ route }: RouteGradeProps) {
return (
<>
{route.isProject && "P"}
{route.difficulty && <Grade difficulty={route.difficulty} />}
</>
);
}

interface RouteStarRatingProps {
route: Route;
size?: IconSize;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { useEffect, useState } from "react";
import difficultyVotesAction from "./server-actions/difficulty-votes-action";
import { DifficultyVote, Route } from "@/graphql/generated";
import displayDate from "@/utils/display-date";
import Grade, { diffToGrade } from "@/components/grade";
import { pluralizeNoun } from "@/utils/text-helpers";
import { gradingSystems } from "@/utils/grading-systems";

interface Props {
route: Route;
difficultyVotes: DifficultyVote[];
}

function DifficultyVotes({ route, difficultyVotes }: Props) {
const routeGradeDifficulty = route.difficulty
? diffToGrade(route.difficulty, "french", false).difficulty
: null;

const nrVotesPerDifficulty = difficultyVotes.reduce(
(acc, vote) => ({
...acc,
[vote.difficulty]: (acc[vote.difficulty] || 0) + 1,
}),
{} as Record<number, number>
);

if (routeGradeDifficulty && !nrVotesPerDifficulty[routeGradeDifficulty]) {
nrVotesPerDifficulty[routeGradeDifficulty] = 0;
}

const maxVotesPerDifficulty = Math.max(
...Object.values(nrVotesPerDifficulty)
);

return (
<>
<table className="w-full max-w-sm">
<tbody>
{Object.entries(nrVotesPerDifficulty).map(([difficulty, nrVotes]) => (
<tr key={difficulty}>
<td className="pr-4">
<Grade
difficulty={parseInt(difficulty)}
displayIntermediate={true}
/>
</td>
<td className="w-full">
<span
className={`float-left block h-5 w-0.5 ${
nrVotes === maxVotesPerDifficulty && "w-full"
} rounded ${
routeGradeDifficulty === parseInt(difficulty)
? "bg-blue-500"
: "bg-neutral-200"
}`}
style={
nrVotes > 0
? {
width: `${Math.round(
(nrVotes / maxVotesPerDifficulty) * 100
)}%`,
}
: {}
}
></span>
<span className="relative float-left">
<span className="absolute whitespace-nowrap pl-3 align-middle text-sm">
{nrVotes !== maxVotesPerDifficulty &&
pluralizeNoun("glas", nrVotes)}
</span>
</span>
</td>
<td className="whitespace-nowrap pl-3 align-middle text-sm">
{nrVotes === maxVotesPerDifficulty &&
pluralizeNoun("glas", nrVotes)}
</td>
</tr>
))}
</tbody>
</table>
<table className="mt-8">
<tbody>
{difficultyVotes.map((vote) => (
<tr
key={vote.id}
className={
vote.includedInCalculation
? "text-neutral-900"
: "text-neutral-500"
}
>
<td className="pr-4">
<Grade
difficulty={vote.difficulty}
displayIntermediate={true}
/>
</td>
<td className="pr-4">
{vote.isBase ? "(bazna ocena)" : vote.user?.fullName}
</td>
<td>{displayDate(vote.created)}</td>
</tr>
))}
</tbody>
</table>
</>
);
}

export default DifficultyVotes;
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { useRouter } from "next/navigation";
import Dialog, {
DialogSize,
} from "../../../../../../../../components/ui/dialog";
import DifficultyVotes from "./difficulty-votes";
import { Crag, DifficultyVote, Route } from "@/graphql/generated";
import Grade from "@/components/grade";
import useIsVisible from "@/hooks/useIsVisible";
import { useEffect, useRef, useState } from "react";
import difficultyVotesAction from "./server-actions/difficulty-votes-action";

interface RouteGradeProps {
route: Route;
crag: Crag;
}

function RouteGrade({ route, crag }: RouteGradeProps) {
const router = useRouter();
const ref = useRef<HTMLButtonElement>(null);
const handleOpenRoutePage = () => {
router.push(`/plezalisce/${crag.slug}/smer/${route.slug}`);
};

const visible = useIsVisible(ref);

const [difficultyVotes, setDifficultyVotes] = useState<DifficultyVote[]>([]);

useEffect(() => {
if (visible) {
difficultyVotesAction(route.id).then((votes) => {
setDifficultyVotes(votes);
});
}
}, [visible, route.id]);

return route.isProject ? (
<>P</>
) : (
<Dialog
openTrigger={
<button ref={ref}>
{route.difficulty && <Grade difficulty={route.difficulty} />}
</button>
}
dialogSize={DialogSize.medium}
title="Glasovi uporabnikov"
confirm={{ label: "Več", callback: handleOpenRoutePage }}
cancel={{ label: "Zapri" }}
>
<DifficultyVotes route={route} difficultyVotes={difficultyVotes} />
</Dialog>
);
}

export default RouteGrade;
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
"use server";

import { gql } from "urql/core";
import {
DifficultyVote,
RouteDifficultyVotesDocument,
} from "@/graphql/generated";
import urqlServer from "@/graphql/urql-server";

async function difficultyVotesAction(
routeId: string
): Promise<DifficultyVote[]> {
const result = await urqlServer().query(RouteDifficultyVotesDocument, {
routeId,
});

if (result.error) {
return Promise.reject(result.error);
}

return result.data.route.difficultyVotes;
}

export default difficultyVotesAction;

gql`
query RouteDifficultyVotes($routeId: String!) {
route(id: $routeId) {
id
slug
difficulty
defaultGradingSystem {
id
}
name
length
difficultyVotes {
user {
id
fullName
firstname
lastname
}
id
difficulty
created
updated
isBase
includedInCalculation
}
}
}
`;
1 change: 0 additions & 1 deletion src/app/[lang]/crags/[countrySlug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ type Params = {
};

async function CragsPage({ params }: { params: Params }) {
console.log("rendering CragsPage");
const { data } = await urqlServer().query(CountryBySlugWithCragsDocument, {
country: params.countrySlug,
input: { type: "sport" },
Expand Down
12 changes: 9 additions & 3 deletions src/components/grade.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ type Props = {

type GradeDisplay = {
name: string;
difficulty?: number;
modifier: -1 | 0 | 1;
};

Expand All @@ -22,16 +23,14 @@ function Grade({
disabled = false,
}: Props) {
const gradeDisplay = diffToGrade(
gradingSystems as GradingSystem[],
difficulty,
gradingSystemId,
displayIntermediate
);
return <>{gradeDisplay.name}</>;
}

function diffToGrade(
gradingSystems: GradingSystem[],
export function diffToGrade(
difficulty: number,
gradingSystemId: string,
legacy: boolean = false
Expand Down Expand Up @@ -62,11 +61,13 @@ function diffToGrade(
if (difficulty <= grades[0].difficulty) {
return {
name: grades[0].name,
difficulty: grades[0].difficulty,
modifier: 0,
};
} else if (difficulty >= grades[grades.length - 1].difficulty) {
return {
name: grades[grades.length - 1].name,
difficulty: grades[grades.length - 1].difficulty,
modifier: 0,
};
}
Expand All @@ -91,11 +92,13 @@ function diffToGrade(
) {
return {
name: curr.name,
difficulty: curr.difficulty,
modifier: 0,
};
} else {
return {
name: curr.name,
difficulty: curr.difficulty,
modifier: -1,
};
}
Expand All @@ -108,17 +111,20 @@ function diffToGrade(
) {
return {
name: curr.name,
difficulty: curr.difficulty,
modifier: 0,
};
} else {
return {
name: curr.name,
difficulty: curr.difficulty,
modifier: +1,
};
}
} else {
return {
name: curr.name,
difficulty: curr.difficulty,
modifier: 0,
};
}
Expand Down
10 changes: 6 additions & 4 deletions src/components/ui/dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,14 @@ function Dialog({
<div className="fixed inset-0 overflow-y-auto p-10">
<DialogHUI.Panel
ref={initFocusRef}
className={`mx-auto rounded-lg bg-white py-10 px-8 shadow-lg ${dialogSize}`}
className={`mx-auto rounded-lg bg-white px-8 py-8 shadow-lg ${dialogSize}`}
>
<DialogHUI.Title as="h4">{title}</DialogHUI.Title>
<DialogHUI.Description className="mt-8" as="div">
{children}
</DialogHUI.Description>
{isOpen && (
<DialogHUI.Description className="mt-8" as="div">
{children}
</DialogHUI.Description>
)}
<div className="mt-10 flex flex-wrap justify-end gap-4">
<Button variant="secondary" onClick={handleCancel}>
{cancel.label}
Expand Down
Loading

0 comments on commit f801909

Please sign in to comment.