Skip to content

Commit

Permalink
Merge pull request #62 from jphacks/fix/yama/user-design
Browse files Browse the repository at this point in the history
ユーザーページのリファクタリング
  • Loading branch information
hikahana authored Nov 15, 2024
2 parents 2145690 + d0cad9a commit 80e491c
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 84 deletions.
10 changes: 5 additions & 5 deletions app/src/app/user/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ const UserPage = () => {
const [myScore, setMyScore] = useState<MyScoreDetail[]>([]);
const [isEditing, setIsEditing] = useState(false);
const [isOpen, setIsOpen] = useStatusChangeDialog();
const handleOpenDialog = () => setIsOpen(true);

useEffect(() => {
const fetchData = async () => {
Expand Down Expand Up @@ -49,7 +48,6 @@ const UserPage = () => {
if (!userData) return null;
return (
<div className="w-screen min-h-screen flex flex-col gap-4 items-center p-4 pt-10 bg-gradient-to-t from-gray-300 via-gray-200 to-gray-50">
{isOpen && <StatusChangeDialog />}
<div className="flex items-center mb-4">
{userData.photoURL ? (
<img
Expand Down Expand Up @@ -112,9 +110,11 @@ const UserPage = () => {
<p className="text-xs text-muted-foreground">最高点</p>
</Card>
</div>
<button type="button" onClick={() => handleOpenDialog()}>
<StatusList speedPoint={10} similarityPoint={40} />
</button>
<StatusList
speedPoint={10}
similarityPoint={40}
onClick={() => setIsOpen(true)}
/>
<Card className="flex flex-col items-center border-none p-8">
<h2 className="text-2xl font-bold mb-4">過去のチャレンジ</h2>
{myScore.length === 0 ? (
Expand Down
13 changes: 9 additions & 4 deletions app/src/components/ui/progress.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,20 @@ import { cn } from "@/lib/utils";

const Progress = React.forwardRef<
React.ElementRef<typeof ProgressPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root>
>(({ className, value, ...props }, ref) => (
React.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root> & {
indicatorClassName?: string;
}
>(({ className, value, indicatorClassName, ...props }, ref) => (
<ProgressPrimitive.Root
ref={ref}
className={cn("relative h-2 w-full overflow-hidden rounded-full bg-primary/20", className)}
className={cn(
"relative h-2 w-full overflow-hidden rounded-full",
className,
)}
{...props}
>
<ProgressPrimitive.Indicator
className="h-full w-full flex-1 bg-primary transition-all"
className={cn("h-full flex-1 transition-all", indicatorClassName)}
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
/>
</ProgressPrimitive.Root>
Expand Down
21 changes: 17 additions & 4 deletions app/src/components/ui/slider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ import { cn } from "@/lib/utils";

const Slider = React.forwardRef<
React.ElementRef<typeof SliderPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>
>(({ className, ...props }, ref) => (
React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root> & {
trackClassName?: string;
thumbClassName?: string;
}
>(({ className, trackClassName, thumbClassName, ...props }, ref) => (
<SliderPrimitive.Root
ref={ref}
className={cn(
Expand All @@ -17,10 +20,20 @@ const Slider = React.forwardRef<
)}
{...props}
>
<SliderPrimitive.Track className="relative h-1.5 w-full grow overflow-hidden rounded-full bg-primary/20">
<SliderPrimitive.Track
className={cn(
"relative h-1.5 w-full grow overflow-hidden rounded-full bg-primary/20",
trackClassName,
)}
>
<SliderPrimitive.Range className="absolute h-full bg-primary" />
</SliderPrimitive.Track>
<SliderPrimitive.Thumb className="block h-4 w-4 rounded-full border border-primary/50 bg-background shadow transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50" />
<SliderPrimitive.Thumb
className={cn(
"block h-4 w-4 rounded-full border border-primary/50 bg-background shadow transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
thumbClassName,
)}
/>
</SliderPrimitive.Root>
));
Slider.displayName = SliderPrimitive.Root.displayName;
Expand Down
105 changes: 69 additions & 36 deletions app/src/components/view/user/StatusChangeDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,55 +4,77 @@ import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { Slider } from "@/components/ui/slider";
import { useStatusChangeDialog } from "@/lib/atom";
import { useState } from "react";
import { useEffect, useState } from "react";

interface StatusChangeDialogProps {
isOpen: boolean;
onClose: () => void;
initialSpeed: number;
initialSimilarity: number;
}

interface PlayStyleSettings {
speed: number;
similarity: number;
total: number;
}

export default function StatusChangeDialog() {
export default function StatusChangeDialog({
isOpen,
onClose,
initialSpeed,
initialSimilarity,
}: StatusChangeDialogProps) {
const [settings, setSettings] = useState<PlayStyleSettings>({
speed: 12,
similarity: 70,
speed: initialSpeed,
similarity: initialSimilarity,
total: 100,
});
const [isOpen, setIsOpen] = useStatusChangeDialog();

// TODO APIの繋ぎ込みをおこなう。その際に値のバリデージョンも実装する。
useEffect(() => {
setSettings({
speed: initialSpeed,
similarity: initialSimilarity,
total: 100,
});
}, [initialSpeed, initialSimilarity]);

const handleSave = async () => {
setIsOpen(false);
onClose();
};

const remainingPoints =
settings.total - (settings.speed + settings.similarity);

return (
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogContent className="max-w-[90vw] sm:max-w-[425px] flex flex-col items-center px-4">
<DialogHeader className="text-center">
<DialogTitle className="text-lg sm:text-xl font-semibold">
<Dialog open={isOpen} onOpenChange={onClose}>
<DialogContent className="p-6 rounded-xl max-w-[90vw]">
<DialogHeader>
<DialogTitle className="text-xl font-semibold">
プレイスタイルの調整
</DialogTitle>
</DialogHeader>
<div className="w-full max-w-[90vw] sm:max-w-[300px] space-y-4 sm:space-y-6 py-4">
<p className="text-sm text-left text-muted-foreground">
<DialogDescription>
指定したポイントを速度と正確性に振り分けてプレイスタイルをカスタマイズできます。
</p>
<div className="space-y-4 sm:space-y-6 px-10">
<div className="space-y-2">
<div className="flex justify-between">
<label htmlFor="speed" className="text-sm font-medium">
</DialogDescription>
</DialogHeader>
<div className="space-y-4">
<div className="flex flex-col items-center">
<div className="space-y-2 w-[80%]">
<div className="flex justify-between items-center">
<label htmlFor="speed" className="font-medium">
スピード
</label>
<span className="text-sm text-muted-foreground">
{settings.speed} / {settings.total}
<span className="text-gray-500 mt-2">
<span className="font-bold text-[#333333]">
{settings.speed}
</span>{" "}
/ {settings.total}
</span>
</div>
<Slider
Expand All @@ -64,16 +86,21 @@ export default function StatusChangeDialog() {
onValueChange={([value]) =>
setSettings({ ...settings, speed: value })
}
className="[&_[role=slider]]:h-4 [&_[role=slider]]:w-4"
className="w-full"
trackClassName="h-3"
thumbClassName="h-6 w-6"
/>
</div>
<div className="space-y-2">
<div className="flex justify-between">
<label htmlFor="similarity" className="text-sm font-medium">
<div className="space-y-2 w-[80%] mt-8">
<div className="flex justify-between items-center">
<label htmlFor="similarity" className="font-medium">
正確性
</label>
<span className="text-sm text-muted-foreground">
{settings.similarity} / {settings.total}
<span className="text-gray-500 mt-2">
<span className="font-bold text-[#333333]">
{settings.similarity}
</span>
/ {settings.total}
</span>
</div>
<Slider
Expand All @@ -85,20 +112,26 @@ export default function StatusChangeDialog() {
onValueChange={([value]) =>
setSettings({ ...settings, similarity: value })
}
className="[&_[role=slider]]:h-4 [&_[role=slider]]:w-4"
className="w-full"
trackClassName="h-3"
thumbClassName="h-6 w-6"
/>
</div>
<div className="flex justify-center items-center text-sm">
<span>未割り当てポイント:</span>
<span className="font-medium ml-1">{remainingPoints}</span>
<div className="flex self-start mt-10">
<span className="font-medium">未割り当てポイント:</span>
<span className="text-green-500 font-medium">
{remainingPoints}
</span>
</div>
</div>
<div className="flex justify-center gap-3 pt-4">
<Button variant="outline" onClick={() => setIsOpen(false)}>
<DialogFooter className="flex flex-row justify-end space-x-2">
<Button variant="outline" onClick={onClose}>
キャンセル
</Button>
<Button onClick={handleSave}>保存</Button>
</div>
<Button className="px-9" onClick={handleSave}>
保存
</Button>
</DialogFooter>
</div>
</DialogContent>
</Dialog>
Expand Down
89 changes: 54 additions & 35 deletions app/src/components/view/user/StatusList.tsx
Original file line number Diff line number Diff line change
@@ -1,55 +1,74 @@
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Progress } from "@/components/ui/progress";
import StatusChangeDialog from "@/components/view/user/StatusChangeDialog";
import { ChevronRight } from "lucide-react";
import { useState } from "react";

type StatusListProps = {
speedPoint: number;
similarityPoint: number;
onClick: () => void;
};

export default function StatusList({
speedPoint,
similarityPoint,
onClick,
}: StatusListProps) {
const [isOpen, setIsOpen] = useState(false);

return (
<Card className="flex flex-col items-center border-none p-8 cursor-pointer">
<CardHeader className="flex flex-row items-center justify-between w-full space-y-0 pb-2">
<CardTitle className="text-xl font-medium">
プレイスタイル設定
</CardTitle>
<ChevronRight className="h-5 w-5 text-muted-foreground" />
</CardHeader>
<CardContent className="w-full pb-6">
<p className="text-sm text-muted-foreground mb-6">
スピードと類似度のバランスを調整
</p>
<div className="flex flex-wrap gap-4">
<div className="flex-1 min-w-[200px] space-y-2">
<div className="flex justify-between text-sm">
<span>スピード</span>
<span>{speedPoint}ポイント</span>
</div>
<Progress value={speedPoint} className="h-2 bg-blue-100">
<div
className="h-full bg-blue-500"
style={{ width: `${speedPoint}%` }}
<>
<Card
className="flex flex-col w-[21rem] items-center border-none cursor-pointer"
onClick={() => {
setIsOpen(true);
onClick();
}}
>
<CardHeader className="flex flex-row items-start justify-between w-full pb-1">
<CardTitle className="text-xl font-bold text-[#333333]">
プレイスタイル設定
</CardTitle>
<ChevronRight className="h-5 w-5 text-muted-foreground" />
</CardHeader>
<CardContent className="w-full pb-6">
<p className="text-start text-sm text-muted-foreground mb-4">
スピードと類似度のバランスを調整
</p>
<div className="flex gap-4">
<div className="w-1/2 space-y-2">
<div className="flex justify-between text-xs">
<span>スピード</span>
<span className="font-bold">{speedPoint}ポイント</span>
</div>
<Progress
value={speedPoint}
className="bg-blue-100"
indicatorClassName="bg-blue-500"
/>
</Progress>
</div>
<div className="flex-1 min-w-[200px] space-y-2">
<div className="flex justify-between text-sm">
<span>類似度</span>
<span>{similarityPoint}ポイント</span>
</div>
<Progress value={similarityPoint} className="h-2 bg-green-100">
<div
className="h-full bg-green-500"
style={{ width: `${similarityPoint}%` }}
<div className="w-1/2 space-y-2">
<div className="flex justify-between text-xs">
<span>類似度</span>
<span className="font-bold">{similarityPoint}ポイント</span>
</div>
<Progress
value={similarityPoint}
className="bg-green-100"
indicatorClassName="bg-green-500"
/>
</Progress>
</div>
</div>
</div>
</CardContent>
</Card>
</CardContent>
</Card>

<StatusChangeDialog
isOpen={isOpen}
onClose={() => setIsOpen(false)}
initialSpeed={speedPoint}
initialSimilarity={similarityPoint}
/>
</>
);
}

0 comments on commit 80e491c

Please sign in to comment.