Skip to content

Commit

Permalink
Merge branch 'main' into feat/kubosaka/form-refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
Kubosaka committed Nov 15, 2024
2 parents 4dbc4eb + 7d5d05e commit b1f152c
Show file tree
Hide file tree
Showing 11 changed files with 12,016 additions and 11,652 deletions.
23,295 changes: 11,646 additions & 11,649 deletions app/package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-progress": "^1.1.0",
"@radix-ui/react-select": "^2.1.2",
"@radix-ui/react-slider": "^1.2.1",
"@radix-ui/react-slot": "^1.1.0",
"browser-image-compression": "^2.0.2",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"date-fns": "^4.1.0",
"firebase": "^11.0.1",
"firebase-admin": "^12.7.0",
"jotai": "^2.10.2",
"lucide-react": "^0.453.0",
"minio": "^8.0.2",
"next": "14.2.16",
Expand Down
7 changes: 7 additions & 0 deletions app/src/app/camera/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Toaster } from "@/components/ui/sonner";
import { PointDialog } from "@/components/view/PointDialog";
import { shapeCaption } from "@/functions/shapeCaption";
import { postSimilarity } from "@/functions/simirality";
import { usePointDialogOpen } from "@/lib/atom";
import type { ScoreResponse, User, todayAssignment } from "@/types";
import imageCompression from "browser-image-compression";
import type React from "react";
Expand Down Expand Up @@ -93,6 +97,7 @@ const CameraApp = () => {
>();
const [assignments, setAssignments] = useState<todayAssignment[]>([]);
const [isActive, setIsActive] = useState<boolean>(true);
const [isPointDialogOpen, setIsPointDialogOpen] = usePointDialogOpen();
const [loginUser, setLoginUser] = useState<User>();

useEffect(() => {
Expand Down Expand Up @@ -256,6 +261,7 @@ const CameraApp = () => {
if (newAssignments.every((assignment) => assignment.isAnswered)) {
setIsActive(false);
}
setIsPointDialogOpen(true);
} catch (error) {
setIsUploading(false);
console.error("アップロード中にエラーが発生しました:", error);
Expand All @@ -282,6 +288,7 @@ const CameraApp = () => {
<>
{isActive ? (
<>
{isPointDialogOpen && <PointDialog type="photo" />}
<div className="flex items-center justify-center">
<Camera
ref={camera}
Expand Down
30 changes: 27 additions & 3 deletions app/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import { Progress } from "@/components/ui/progress";
import { PointDialog } from "@/components/view/PointDialog";
import Timer from "@/components/view/Timer";
import { useHasShownOnce, usePointDialogOpen } from "@/lib/atom";
import type { MyScoreDetail, todayAssignment } from "@/types";
import { useRouter } from "next/navigation";
import { useEffect, useState } from "react";
Expand All @@ -16,6 +18,10 @@ export default function Home() {
const [isLoading, setIsLoading] = useState(true);
const [progressCount, setProgressCount] = useState(0);
const router = useRouter();
const [_, setIsPointDialogOpen] = usePointDialogOpen();
const [hasShownOnce, setHasShownOnce] = useHasShownOnce();

// TODO:ログインモーダルが表示されるようにする

useEffect(() => {
const fetchData = async () => {
Expand Down Expand Up @@ -68,8 +74,19 @@ export default function Home() {
fetchData();
}, []);

// 初回開いたらlocalstorageに保管している
// todo 最初のログイン時で開閉を行う。例:firebaseでログイン日を取得できるため今日と一致しているかを比較する.updatedATにログイン日を入れてもいいかも
// todo ポイント付与api繋ぎ込み
useEffect(() => {
if (!hasShownOnce) {
setIsPointDialogOpen(true);
setHasShownOnce(true);
}
});

return (
<div className="flex flex-col h-full px-10 py-10 bg-gradient-to-t from-gray-300 via-gray-200 to-gray-50">
<PointDialog type="login" />
<div className="flex flex-col items-center justify-center space-y-6">
{isLoading ? (
<Card className="w-full py-3 px-7">
Expand Down Expand Up @@ -97,7 +114,9 @@ export default function Home() {
<h2 className="text-lg font-semibold mb-2">今日のお題</h2>
<p className="text-sm text-gray-600">撮影してスコアを競おう!</p>
</div>
<h1 className="text-3xl font-bold text-center mb-4">{assignment[0]?.english}</h1>
<h1 className="text-3xl font-bold text-center mb-4">
{assignment[0]?.english}
</h1>
<div className="flex justify-center w-full">
<Button
variant="default"
Expand All @@ -116,11 +135,16 @@ export default function Home() {
{myScore.length === 0 ? (
<div className="text-gray-500 text-center py-8">
<p>まだチャレンジの記録がありません</p>
<p className="text-sm mt-2">新しいチャレンジに挑戦してみましょう!</p>
<p className="text-sm mt-2">
新しいチャレンジに挑戦してみましょう!
</p>
</div>
) : (
myScore.map((score) => (
<div key={score.id} className="flex w-full items-center mb-2 border rounded-md">
<div
key={score.id}
className="flex w-full items-center mb-2 border rounded-md"
>
<img
src={score.imageUrl || "https://placehold.jp/150x150.png"}
alt="チャレンジ画像"
Expand Down
13 changes: 13 additions & 0 deletions app/src/app/ranking/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
"use client";
import { Button } from "@/components/ui/button";
import { PointDialog } from "@/components/view/PointDialog";
import { ImageList } from "@/components/view/ranking/ImageList";
import { usePointDialogOpen } from "@/lib/atom";
import type { todayAssignment } from "@/types";
import { useEffect, useState } from "react";
import { BsGrid1X2 } from "react-icons/bs";
Expand All @@ -17,6 +19,11 @@ export default function RankingPage() {
const [rankingType, setRankingType] = useState<"nomal" | "image">("nomal");
const [selectedTopic, setSelectedTopic] = useState(0);
const [topics, setTopics] = useState<todayAssignment[]>([]);
const [isPointDialogOpen, setIsPointDialogOpen] = usePointDialogOpen();

const handleClickOpen = () => {
setIsPointDialogOpen(true);
};

useEffect(() => {
const fetchTopics = async () => {
Expand All @@ -33,8 +40,14 @@ export default function RankingPage() {
fetchTopics();
}, []);

// TODO 結果発表のタイミングに修正する。
useEffect(() => {
handleClickOpen();
});

return (
<div className="min-h-screen bg-gradient-to-t from-gray-300 via-gray-200 to-gray-50 px-4">
{isPointDialogOpen && <PointDialog type="ranking" />}
{rankingType === "nomal" && (
<>
<div className="w-full flex flex-col items-center justify-center sticky top-0 z-10 pt-4">
Expand Down
9 changes: 9 additions & 0 deletions app/src/app/user/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import StatusChangeDialog from "@/components/view/user/StatusChangeDialog";
import StatusList from "@/components/view/user/StatusList";
import { useStatusChangeDialog } from "@/lib/atom";
import type { MyScoreDetail, User } from "@/types";
import { useEffect, useState } from "react";
import { FiEdit2 } from "react-icons/fi";
Expand All @@ -13,6 +16,8 @@ const UserPage = () => {
const [userData, setUserData] = useState<User>();
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 @@ -44,6 +49,7 @@ 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 @@ -106,6 +112,9 @@ const UserPage = () => {
<p className="text-xs text-muted-foreground">最高点</p>
</Card>
</div>
<button type="button" onClick={() => handleOpenDialog()}>
<StatusList speedPoint={10} similarityPoint={40} />
</button>
<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
28 changes: 28 additions & 0 deletions app/src/components/ui/slider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"use client";

import * as SliderPrimitive from "@radix-ui/react-slider";
import * as React from "react";

import { cn } from "@/lib/utils";

const Slider = React.forwardRef<
React.ElementRef<typeof SliderPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>
>(({ className, ...props }, ref) => (
<SliderPrimitive.Root
ref={ref}
className={cn(
"relative flex w-full touch-none select-none items-center",
className,
)}
{...props}
>
<SliderPrimitive.Track className="relative h-1.5 w-full grow overflow-hidden rounded-full bg-primary/20">
<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.Root>
));
Slider.displayName = SliderPrimitive.Root.displayName;

export { Slider };
99 changes: 99 additions & 0 deletions app/src/components/view/PointDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
"use client";

import { Calendar, Camera, Trophy, X } from "lucide-react";
import type * as React from "react";

import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { useState } from "react";
import { useHasShownOnce } from "../../lib/atom";

interface NotificationDialogProps {
type: "login" | "photo" | "ranking";
customTitle?: string;
customMessage?: string;
customSubMessage?: string;
}

export function PointDialog({
type = "login",
customTitle,
customMessage,
customSubMessage,
}: NotificationDialogProps) {
const [hasShownOnce, _] = useHasShownOnce();
const [isOpen, setIsOpen] = useState<boolean>(hasShownOnce);

const content = {
login: {
title: customTitle || "ログイン継続中!",
icon: Calendar,
iconColor: "text-blue-500",
message: customMessage || "1日目",
subMessage: customSubMessage || "ポイントを獲得しました!",
},
photo: {
title: customTitle || "撮影に成功しました!",
icon: Camera,
iconColor: "text-green-500",
message: customMessage || "素晴らしい写真です!",
subMessage: customSubMessage || "ポイントを獲得しました!",
},
ranking: {
title: customTitle || "ランキング結果発表!",
icon: Trophy,
iconColor: "text-yellow-500",
message: customMessage || "第10位",
subMessage: customSubMessage || "ポイントを獲得しました!",
},
};

const {
title: dialogTitle,
icon: Icon,
iconColor,
message,
subMessage,
} = content[type];

return (
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogContent className="sm:max-w-md max-w-xs mx-auto px-4 rounded-lg">
<DialogHeader>
<Button
variant="ghost"
className="absolute right-4 top-4 h-4 w-4 p-0"
onClick={() => setIsOpen(false)}
>
<X className="h-4 w-4" />
</Button>
</DialogHeader>
<DialogTitle>
<div className="space-y-2 text-center mt-4">
<h2 className="text-xl font-semibold">{dialogTitle}</h2>
</div>
</DialogTitle>
<div className="flex flex-col items-center gap-4">
<div className={`rounded-full p-3 ${iconColor} bg-opacity-10`}>
<Icon className={`h-10 w-10 ${iconColor}`} />
</div>
<div className="space-y-2 text-center">
<p className="text-sm text-muted-foreground">{message}</p>
<p className="text-sm text-muted-foreground">{subMessage}</p>
</div>
<Button
className="mt-4 w-full max-w-[200px] rounded-full"
onClick={() => setIsOpen(false)}
>
閉じる
</Button>
</div>
</DialogContent>
</Dialog>
);
}
Loading

0 comments on commit b1f152c

Please sign in to comment.