Skip to content
This repository has been archived by the owner on May 4, 2024. It is now read-only.

사용자가 시작 버튼으로 시작가능 #5

Merged
merged 4 commits into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions src/app/activities/[activity-id]/start/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import BasicLayout from "~/components/ui/BasicLayout";
import { Textarea } from "~/components/ui/textarea";
import { getActivity } from "~/models/activity";

export default function ActivitiesStartPage({
params,
}: {
params: { "activity-id": string };
}) {
return (
<BasicLayout>
<div className="flex flex-col items-center w-full gap-y-4">
<Timer />
<Editor activityId={params["activity-id"]} />
</div>
</BasicLayout>
);
}

function Timer() {
return (
<>
00:00:00
<hr className="text-gray-500 w-full" />
</>
);
}

async function Editor({ activityId }: { activityId: string }) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Editor function component is defined as async, which is not typical for React components and can lead to unintended behavior. React components should not be asynchronous. Consider fetching data outside of the component or using effects/hooks for asynchronous operations.

- async function Editor({ activityId }: { activityId: string }) {
+ function Editor({ activityId }: { activityId: string }) {
+   // Use useEffect or another method to handle asynchronous operations

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
async function Editor({ activityId }: { activityId: string }) {
function Editor({ activityId }: { activityId: string }) {
// Use useEffect or another method to handle asynchronous operations

if (!activityId) {
return "존재하지 않는 활동이에요!";
}

const activity = await getActivity({ activityId });

return (
<section className="w-full">
<div className="pb-4">
<h1 className="font-bold">{activity.name}</h1>
<p className="text-sm text-gray-500">{activity.description}</p>
</div>
<Textarea autoFocus className="h-[80vh]" />
</section>
);
}
6 changes: 3 additions & 3 deletions src/app/activities/features/ListCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
CardTitle,
} from "~/components/ui/card";
import PlayButton from "./PlayButton";
import type { Activity } from "~/models/activity";
import { type Activity } from "~/models/activity";

export default function ListCard({ activity }: { activity: Activity }) {
return (
Expand All @@ -16,9 +16,9 @@ export default function ListCard({ activity }: { activity: Activity }) {
<CardTitle>{activity.name}</CardTitle>
<CardDescription>{activity.description}</CardDescription>
</CardHeader>
<CardContent></CardContent>
<CardContent />
<CardFooter className="justify-end">
<PlayButton />
<PlayButton activity={activity} />
</CardFooter>
</Card>
);
Expand Down
23 changes: 21 additions & 2 deletions src/app/activities/features/PlayButton.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,28 @@
"use client";

import { useRouter } from "next/navigation";
import React from "react";
import { PlayIcon } from "~/components/icons";
import { Button } from "~/components/ui/button";
import { Activity, startActivity } from "~/models/activity";

export default function PlayButton(
props: React.ComponentPropsWithoutRef<"button"> & { activity: Activity },
) {
const { activity, ...rest } = props;

const router = useRouter();

async function handleStartActivity() {
await startActivity({
activity: { ...activity, started_at: new Date().getTime() },
});

router.push(`/activities/${activity.id}/start`);
}

export default function PlayButton() {
return (
<Button>
<Button {...rest} onClick={handleStartActivity}>
<PlayIcon />
</Button>
);
Expand Down
5 changes: 3 additions & 2 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import AddButton from "~/app/activities/features/AddButton";
import AcgivityList from "./activities/widgets/ActivityList";
import BasicLayout from "~/components/ui/BasicLayout";

export default async function HomePage() {
return (
<main className="flex max-w-96 m-auto min-h-screen flex-col items-center justify-between p-8">
<BasicLayout>
<div className="flex flex-col items-center w-full gap-y-4">
00:00:00
<hr className="text-gray-500 w-full" />
<AddButton />
<AcgivityList />
</div>
</main>
</BasicLayout>
);
}
13 changes: 13 additions & 0 deletions src/components/ui/BasicLayout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from "react";

export default function BasicLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<main className="flex max-w-96 m-auto min-h-screen flex-col items-center justify-between p-8">
{children}
</main>
);
}
24 changes: 24 additions & 0 deletions src/components/ui/textarea.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import * as React from "react"

import { cn } from "~/lib/utils"

export interface TextareaProps
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}

const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
({ className, ...props }, ref) => {
return (
<textarea
className={cn(
"flex min-h-[60px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
className
)}
ref={ref}
{...props}
/>
)
}
)
Textarea.displayName = "Textarea"

export { Textarea }
23 changes: 20 additions & 3 deletions src/mocks/http.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import express from "express";
import cors from "cors";
import { Activity } from "~/models/activity";

const app = express();
const port = 9090;
Expand All @@ -13,13 +14,13 @@ const genId = () => {
count = (count + 1) % Number.MAX_SAFE_INTEGER;
return count.toString();
};

const data = {
const data: { activities: Activity[] } = {
activities: [
{
id: "0",
name: "사이드 프로젝트/giroker",
description: "시간단위로 기록하는 프로젝트",
started_at: -1,
},
],
};
Expand All @@ -30,11 +31,27 @@ const PATHS = {

app.listen(port, () => console.log(`Mock server is running on port: ${port}`));

app.get(PATHS.activities + "/:activity_id", (req, res) => {
const id = req.params["activity_id"];
const activity = data.activities.find((activity) => activity.id === id);

res.json({ data: activity });
});

app.get(PATHS.activities, (_, res) => {
res.json({ data });
});

app.post(PATHS.activities, (req, res) => {
data.activities.unshift({ id: genId(), ...req.body });
data.activities.unshift({ id: genId(), started_at: -1, ...req.body });
res.status(201).end();
});

app.post(PATHS.activities + "/:activity_id/start", (req, res) => {
const id = req.params["activity_id"];
const index = data.activities.findIndex((activity) => activity.id === id);

data.activities[index] = { ...req.body };

res.end();
});
29 changes: 27 additions & 2 deletions src/models/activity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ type Activity = {
id: string;
name: string;
description: string;
started_at: number;
};

export const ACTIVITES = "activities";
Expand All @@ -19,6 +20,18 @@ const activitySchema = z.object({
description: z.string().max(80, { message: "80자 이하로 설정해주세요" }),
});

async function getActivity({
activityId,
}: {
activityId: Activity["id"];
}): Promise<Activity> {
const result = await fetch(`${API_ENDPOINT}/${ACTIVITES}/${activityId}`, {
cache: "no-store",
}).then((d) => d.json());

return result.data;
}

async function getActivities(): Promise<Activity[]> {
const result = await fetch(`${API_ENDPOINT}/${ACTIVITES}`, {
cache: "no-store",
Expand All @@ -29,7 +42,6 @@ async function getActivities(): Promise<Activity[]> {

async function createActivity({ body }: { body: Omit<Activity, "id"> }) {
await fetch(`${API_ENDPOINT}/${ACTIVITES}`, {
cache: "no-store",
method: "post",
headers: {
Accept: "application/json",
Expand All @@ -39,10 +51,23 @@ async function createActivity({ body }: { body: Omit<Activity, "id"> }) {
});
}

async function startActivity({ activity }: { activity: Activity }) {
await fetch(`${API_ENDPOINT}/${ACTIVITES}/${activity.id}/start`, {
method: "post",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify(activity),
});
}

export {
type Activity,
type ActivitySchema,
activitySchema,
createActivity,
getActivity,
getActivities,
createActivity,
startActivity,
};
Loading