Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Selectgame UI #8

Merged
merged 17 commits into from
Sep 2, 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
13 changes: 8 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
https://www.figma.com/design/8NGJOCtcVqLFtTrS3P0ht7/Nihongo-Alley---copied-with-new-games?node-id=0-88&node-type=CANVAS&t=FZORUYPPwlmsROs0-0

## Folder structure
There is two ways in Nextjs. We adopt pages directory structure. <br>
There is two ways in Nextjs for routing. We adopt pages routing, not app routing. <br>
https://medium.com/@CraftedX/should-you-use-next-js-pages-or-app-directory-38e803fe5cb4

Folder structure is related to routing.<br>
Expand All @@ -17,10 +17,10 @@ The basic behavior of Next.js by naming for file/folders.<br>
There are some part which is defferent in between page router and app router.<br>
https://nextjs.org/docs/app/building-your-application/routing/colocation<br>

The _app.tsx file is used for every page.<br>
The _app.tsx file is used for rendering every page.<br>
https://nextjs.org/docs/pages/building-your-application/routing/custom-app

Page name is case-sensitive.
Page name is case-sensitive. URL is case-sensitive as well.

* Commponents<br>
React components used in each page. For example, flash card, card to show score, ...<br>
Expand All @@ -39,22 +39,25 @@ https://next-auth.js.org/getting-started/example<br>
* install<br>
npm install<br>
※For development of only frontend, we don't need docker run.<br>
※EbisuG haven't checked running app with docker yet.

* run local<br>
npm run dev<br>

## Route
### Protected route
Use middleware.ts for protected route.
https://www.freecodecamp.org/news/secure-routes-in-next-js/<br>
※EbisuG haven't implemented protecton for some route yet.

## Authentication
We use Nextauth.js.
Detail logic.
https://refine.dev/blog/nextauth-google-github-authentication-nextjs/#for-githubprovider-you-will-need-a-github-account


## Errors
* App doesn't run after successful build.
Make sure you use the latest things. Delete your image, container, and volume. Run docker compose commands again.

### Reference
Using google auth.<br>
https://blog.stackademic.com/building-a-custom-google-authentication-system-with-django-rest-framework-and-reactjs-ii-794fa8592782
Expand Down
Empty file.
27 changes: 27 additions & 0 deletions components/game/newExpression/mainCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { useContext } from "react"
import { SelectedMaterial } from "../../../pages/game/learning"

//
const getPhrase = async (id: number) => {
const result = "test string"
// const fetched = await fetch("endopinturl")
// const result: phraseType = fetched.json()
return result
}

const MainCard = () => {
const selectedMaterial = useContext(SelectedMaterial)

return (
<>
<div className="block max-w-full p-6 m-10 bg-white border border-gray-200 rounded-lg shadow
hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700">
{selectedMaterial?.phrase!==undefined?(<>{selectedMaterial.phrase.japanese}</>):<>null</>}<br></br>
{selectedMaterial?.phrase!==undefined?(<>{selectedMaterial.phrase.english}</>):<>null</>}<br></br>
{selectedMaterial?.phrase!==undefined?(<>{selectedMaterial.phrase.description}</>):<>null</>}<br></br>
</div>
</>
)
}

export default MainCard
50 changes: 50 additions & 0 deletions components/game/newExpression/phraseList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { useContext } from "react"
import { SelectedMaterial } from "../../../pages/game/learning"

//get all phrase based on topic
const getAllPhrase = async (id: number) => {
const result = "test string"
// const fetched = await fetch("endopinturl")
// const result: phraseType = fetched.json()
return result
}

const PhraseList = () => {
const selectedMaterial = useContext(SelectedMaterial)

return (
<>
<div className="block max-w-full p-6 m-10 bg-white border border-gray-200 rounded-lg shadow
hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700"
onClick={()=>{selectedMaterial?.selectPhrase({id:2, japanese:"よっす"})}}
>
topic is :{selectedMaterial?.topic}
よっす
</div>
<div className="block max-w-full p-6 m-10 bg-white border border-gray-200 rounded-lg shadow
hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700"
onClick={()=>{selectedMaterial?.selectPhrase({id:3, japanese:"おっす", english:"hi", description:"short version of おはようございます"})}}
>
topic is :{selectedMaterial?.topic}
おっす
</div>
<div className="block max-w-full p-6 m-10 bg-white border border-gray-200 rounded-lg shadow
hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700">
topic is :{selectedMaterial?.topic}
うっす
</div>
<div className="block max-w-full p-6 m-10 bg-white border border-gray-200 rounded-lg shadow
hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700">
topic is :{selectedMaterial?.topic}
はいよ
</div>
<div className="block max-w-full p-6 m-10 bg-white border border-gray-200 rounded-lg shadow
hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700">
topic is :{selectedMaterial?.topic}
わかった!
</div>
</>
)
}

export default PhraseList
27 changes: 27 additions & 0 deletions components/game/newExpression/start.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import Image, { ImageLoader } from 'next/image'
import gamePic from "./../../../public/new-expression-fox.png"
import Link from 'next/link'

const baseUrl = process.env.NEXTAUTH_URL

const imageLoader: ImageLoader = ({ src, width, quality }) => {
return `${baseUrl}/${src}?w=${width}&q=${quality || 75}`
}

const LearningStart: React.FC = () => {
return (<>
<div className="grid grid-cols-2 gap-4">
<div>
<span>New Expression</span><br></br>
<Link href={"/game/learning"}>
<span> Learning Mode</span>
</Link>
</div>
<div>
<Image loader={imageLoader} width={300} height={300} src={gamePic} alt='Fox picture' />
</div>
</div>
</>)
}

export default LearningStart
24 changes: 24 additions & 0 deletions components/game/practice/start.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import Image, { ImageLoader } from 'next/image'
import gamePic from "./../../../public/practice-nife.png"

const baseUrl = process.env.NEXTAUTH_URL

const imageLoader: ImageLoader = ({ src, width, quality }) => {
return `${baseUrl}/${src}?w=${width}&q=${quality || 75}`
}

const PracticeStart: React.FC = () => {
return (<>
<div className="grid grid-cols-2 gap-4">
<div>
<span>Practice</span><br></br>
<span> Practice Mode</span>
</div>
<div>
<Image loader={imageLoader} width={300} height={300} src={gamePic} alt='Cross nife picture' />
</div>
</div>
</>)
}

export default PracticeStart
8 changes: 6 additions & 2 deletions components/header.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useSession, signIn, signOut } from "next-auth/react";
import { useSession, signOut } from "next-auth/react";
import Link from "next/link";
import { useRouter } from "next/router";

Expand All @@ -9,7 +9,11 @@ export const Header: React.FC = () => {
return (
<>
<div className="flex flex-row">
<div className="basis-1/6">To Homepage</div>
<div className="basis-1/6">
<Link href={"/"}>
To Homepage
</Link>
</div>
<div className="basis-3/6">This is empty space</div>
{session ?
<>
Expand Down
6 changes: 5 additions & 1 deletion next.config.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
/** @type {import('next').NextConfig} */
const nextConfig = {};
const nextConfig = {
experimental: {
optimizePackageImports: ["next-auth"],
},
};

export default nextConfig;
2 changes: 0 additions & 2 deletions pages/404.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
//User can play quick answer game.

import React, { useState, useEffect } from "react";

const BACKEND_URL = process.env.NEXT_PUBLIC_BACKEND_URL;

const Error404 = () => {
Expand Down
1 change: 0 additions & 1 deletion pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from "react";
import { AppProps } from "next/app";
import "../styles/globals.css";
import { Layout } from "../components/layouts";
Expand Down
6 changes: 6 additions & 0 deletions pages/api/auth/[...nextauth].ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ let googleClientSecret = process.env.GOOGLE_CLIENT_SECRET
if (googleClientId === undefined) googleClientId = ""
if (googleClientSecret === undefined) googleClientSecret = ""

interface urlSets {
url: string
baseUrl: string
}

export const authOptions = {
// Configure one or more authentication providers
providers: [
Expand All @@ -18,6 +23,7 @@ export const authOptions = {
// ...add more providers here
],
secret: process.env.NEXTAUTH_SECRET,
//add calbacks here.
}

export default NextAuth(authOptions)
52 changes: 48 additions & 4 deletions pages/game/learning.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,60 @@
//User can play learning page.

import React, { useState, useEffect } from "react";
import React, { useState, useEffect, createContext } from "react";
import MainCard from "../../components/game/newExpression/mainCard";
import { phraseType, learningContextType } from "../../types/types";
import PhraseList from "../../components/game/newExpression/phraseList";

const BACKEND_URL = process.env.NEXT_PUBLIC_BACKEND_URL;
const TOPICS = ["First Contact", "Playing together", "Drinking"]
export const SelectedMaterial = createContext<learningContextType | null>(null)

const Learning = () => {
const [selectedTopic, setSelectedTopic] = useState(TOPICS[0])
const [selectedPhrase, setSelectedPhrase] = useState<phraseType>({
id: 0,
japanese: "loading...",
})

//Just test for appearence, put useEffect here.
useEffect(() => {
setSelectedPhrase({
id: 1,
japanese: "よっす",
english: "Hi",
description: "よっす is shorter version of おはようございます"
})
}, [])

return (
<div>
This is Learning.
</div>
<>
<SelectedMaterial.Provider value={{ topic: selectedTopic, phrase: selectedPhrase, selectPhrase: setSelectedPhrase }}>
<div className="grid grid-cols-3 gap-4">
<div>
Main card component
<MainCard></MainCard>
</div>
<div>
Choose topic
{TOPICS.map((elem, ind) => {
return (
<div className="block max-w-sm p-6 m-10 bg-white border border-gray-200 rounded-lg shadow
hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700" key={ind}
onClick={() => {
setSelectedTopic(elem)
}}>
{elem}
</div>
)
})}
</div>
<div>
Each pharese list
<PhraseList></PhraseList>
</div>
</div>
</SelectedMaterial.Provider>
</>
);
};

Expand Down
4 changes: 3 additions & 1 deletion pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { useState, useEffect } from "react";
import Link from "next/link";
import { useState, useEffect } from "react";

const BACKEND_URL = process.env.NEXT_PUBLIC_BACKEND_URL;

Expand All @@ -19,6 +20,7 @@ const HomePage = () => {
<h3>With Django, React, Postgres, and Docker</h3>
<p>{data ? data : "Loading data..."}</p>{" "}
{/* Display data or loading message */}
<Link href={"/selectGame"}> Play game!</Link>
</div>
);
};
Expand Down
5 changes: 3 additions & 2 deletions pages/login.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
//This page is for login page.
//User can login and sign up.

import React, { useState, useEffect, FormEvent } from "react";
import SelectGame from "./selectGame";
import { useState, FormEvent } from "react";
import { useRouter } from "next/router";
import { useSession, signIn, signOut } from "next-auth/react";
import Link from "next/link";

const BACKEND_URL = process.env.NEXT_PUBLIC_BACKEND_URL;

Expand Down Expand Up @@ -40,6 +40,7 @@ const Login = () => {
) : (
<>
Alredy logged in.
<Link href={"/selectGame"}>Play games!</Link>
</>
))
}
Expand Down
38 changes: 33 additions & 5 deletions pages/selectGame.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,44 @@
//User can select game.

import React, { useState, useEffect } from "react";
import React, { useState } from "react";
import NewExpressionStart from "../components/game/newExpression/start";
import PracticeStart from "../components/game/practice/start";

const BACKEND_URL = process.env.NEXT_PUBLIC_BACKEND_URL;
const gameFileNames = ["newExpression", "practice"]

const SelectGame = () => {
const [gameName, setGameName] = useState(gameFileNames[0])

const goRightGame = () => {
setGameName(gameFileNames[(gameFileNames.indexOf(gameName) + 1) % gameFileNames.length])
}
const goLeftGame = () => {
setGameName(gameFileNames[(gameFileNames.indexOf(gameName) + gameFileNames.length - 1) % gameFileNames.length])
}

const showGameTitle = (gameFileName: string) => {
switch (gameFileName) {
case "newExpression":
return <NewExpressionStart></NewExpressionStart>
case "practice":
return <PracticeStart></PracticeStart>
default:
return (
<>Sorry, there is no game.</>
)
}
}


return (
<div>
This is select game page.
</div>
<>
<button onClick={goLeftGame}>go right</button>
<div>
This is select game page.<br />
{showGameTitle(gameName)}
</div>
<button onClick={goRightGame}>go left</button>
</>
);
};

Expand Down
Binary file added public/new-expression-fox.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/practice-nife.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading