INIT(app): initial commit

- Initialize project structure
- Add base configuration
This commit is contained in:
2025-11-22 23:44:51 +09:00
commit 3c10907a97
41 changed files with 7170 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
{
"extends": "next/core-web-vitals"
}

36
services/nextjs/README.md Normal file
View File

@@ -0,0 +1,36 @@
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
## Getting Started
First, run the development server:
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.

View File

@@ -0,0 +1,5 @@
"use client"
export default function Error(){
return <h1>Something broke...</h1>
}

View File

@@ -0,0 +1,3 @@
export default function Loading() {
return <h2>Loading a movie :id</h2>;
}

View File

@@ -0,0 +1,26 @@
import { Suspense } from "react";
import MovieInfo from "@/components/movie-info";
import VideosInfo from "@/components/movie-videos";
import { getMovie } from "@/components/movie-info";
interface Iparams {
params: { id: string };
}
export async function generateMetadata({ params: { id } }: Iparams) {
const movie = await getMovie(id);
return { title: movie.title };
}
export default async function MovieDetail({ params: { id } }: Iparams) {
return (
<div>
<Suspense fallback={<h1>Loading movie info</h1>}>
<MovieInfo id={id} />
</Suspense>
<Suspense fallback={<h1>Loading videos info</h1>}>
<VideosInfo id={id} />
</Suspense>
</div>
);
}

View File

@@ -0,0 +1,3 @@
export default function AboutUs(){
return <div>gfdgd</div>
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -0,0 +1,18 @@
import "../styles/global.css";
import React from "react";
import Navigation from "@/components/navigation";
export const metadata = {
title: "movieClone",
};
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<Navigation></Navigation>
{children}
</body>
</html>
);
}

View File

@@ -0,0 +1,30 @@
import styles from "../styles/home.module.css";
import Movie from "@/components/movie";
const URL = "https://nomad-movies.nomadcoders.workers.dev/movies";
export const metadata = {
title: "Jovies",
};
async function getMovies() {
const response = await fetch(URL);
const json = await response.json();
return json;
}
export default async function HomePage() {
const movies = await getMovies();
return (
<div className={styles.container}>
{movies.map((movie: any) => (
<Movie
key={movie.id}
id={movie.id}
poster_path={movie.poster_path}
title={movie.title}
></Movie>
))}
</div>
);
}

View File

@@ -0,0 +1,29 @@
import styles from "../styles/movie-info.module.css";
const URL = "https://nomad-movies.nomadcoders.workers.dev/movies";
export async function getMovie(id: string) {
const response = await fetch(`${URL}/${id}`);
return response.json();
}
export default async function MovieInfo({ id }: { id: string }) {
const movie = await getMovie(id);
return (
<div className={styles.container}>
<img
src={movie.poster_path}
className={styles.poster}
alt={movie.title}
/>
<div className={styles.info}>
<h1 className={styles.title}>{movie.title}</h1>
<h3>*{movie.vote_average.toFixed(1)}</h3>
<p>{movie.overview}</p>
<a href={movie.homepage} target={"_blank"}>
Homepage &rarr;
</a>
</div>
</div>
);
}

View File

@@ -0,0 +1,25 @@
import styles from "../styles/movie-videos.module.css";
const URL = "https://nomad-movies.nomadcoders.workers.dev/movies";
async function getVideos(id: string) {
const response = await fetch(`${URL}/${id}/videos`);
return response.json();
}
export default async function VideosInfo({ id }: { id: string }) {
const videos = await getVideos(id);
return (
<div className={styles.container}>
{videos.map((video: any) => (
<iframe
key={video.id}
src={`https://youtube.com/embed/${video.key}`}
title={video.name}
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
/>
))}
</div>
);
}

View File

@@ -0,0 +1,24 @@
"use client";
import Link from "next/link";
import styles from "../styles/movie.module.css";
import { useRouter } from "next/navigation";
interface IMovieProps {
title: string;
id: string;
poster_path: string;
}
export default function Movie({ title, id, poster_path }: IMovieProps) {
const router = useRouter();
const onClick = () => {
router.push(`/movies/${id}`);
};
return (
<div className={styles.movie}>
<img src={poster_path} alt={title} onClick={onClick} />
<Link href={`/movies/${id}`}>{title}</Link>
</div>
);
}

View File

@@ -0,0 +1,23 @@
"use client";
import Link from "next/link";
import { usePathname } from "next/navigation";
import styles from "../styles/navigation.module.css"
export default function Navigation() {
const path = usePathname();
return (
<nav className={styles.nav}>
<ul>
<li>
<Link href="/">Home</Link>
{path === "/" ? "🔥" : ""}
</li>
<li>
<Link href="/about-us">About Us</Link>
{path === "/about-us" ? "🔥" : ""}
</li>
</ul>
</nav>
);
}

View File

@@ -0,0 +1,6 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'standalone',
};
export default nextConfig;

5478
services/nextjs/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,26 @@
{
"name": "nextjstutorial",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"react": "^18",
"react-dom": "^18",
"next": "14.2.3"
},
"devDependencies": {
"typescript": "^5",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"postcss": "^8",
"tailwindcss": "^3.4.1",
"eslint": "^8",
"eslint-config-next": "14.2.3"
}
}

View File

@@ -0,0 +1,8 @@
/** @type {import('postcss-load-config').Config} */
const config = {
plugins: {
tailwindcss: {},
},
};
export default config;

View File

@@ -0,0 +1,154 @@
html,
body,
div,
span,
applet,
object,
iframe,
h1,
h2,
h3,
h4,
h5,
h6,
p,
blockquote,
pre,
a,
abbr,
acronym,
address,
big,
cite,
code,
del,
dfn,
em,
img,
ins,
kbd,
q,
s,
samp,
small,
strike,
strong,
sub,
sup,
tt,
var,
b,
u,
i,
center,
dl,
dt,
dd,
ol,
ul,
li,
fieldset,
form,
label,
legend,
table,
caption,
tbody,
tfoot,
thead,
tr,
th,
td,
article,
aside,
canvas,
details,
embed,
figure,
figcaption,
footer,
header,
hgroup,
menu,
nav,
output,
ruby,
section,
summary,
time,
mark,
audio,
video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
menu,
nav,
section {
display: block;
}
body {
line-height: 1;
}
ol,
ul {
list-style: none;
}
blockquote,
q {
quotes: none;
}
blockquote:before,
blockquote:after,
q:before,
q:after {
content: "";
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
body {
padding-top: 150px;
background-color: black;
color: white;
font-size: 18px;
font-family:
system-ui,
-apple-system,
BlinkMacSystemFont,
"Segoe UI",
Roboto,
Oxygen,
Ubuntu,
Cantarell,
"Open Sans",
"Helvetica Neue",
sans-serif;
}
a {
color: inherit;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}

View File

@@ -0,0 +1,8 @@
.container {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 25px;
max-width: 90%;
width: 100%;
margin: 0 auto;
}

View File

@@ -0,0 +1,27 @@
.container {
display: grid;
grid-template-columns: 1fr 2fr;
gap: 50px;
width: 80%;
margin: 0 auto;
}
.poster {
border-radius: 20px;
max-width: 70%;
place-self: center;
}
.title {
color: white;
font-size: 36px;
font-weight: 600;
}
.info {
display: flex;
flex-direction: column;
margin-top: 20px;
gap: 20px;
}

View File

@@ -0,0 +1,19 @@
.container {
width: 80%;
margin: 0 auto;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin-top: 100px;
padding-bottom: 100px;
}
.container iframe {
border-radius: 10px;
opacity: 0.8;
transition: opacity 0.2s ease-in-out;
}
.container iframe:hover {
opacity: 1;
}

View File

@@ -0,0 +1,26 @@
.movie {
display: grid;
grid-template-rows: 1fr auto;
gap: 20px;
cursor: pointer;
place-items: center;
}
.movie img {
max-width: 100%;
min-height: 100%;
border-radius: 10px;
transition: opacity 0.3s ease-in-out;
}
.movie img {
opacity: 0.7;
}
.movie img:hover {
opacity: 1;
}
.movie a {
text-align: center;
}

View File

@@ -0,0 +1,28 @@
.nav{
background-color: #2d2d2d;
position: fixed;
width: 30%;
margin: 0 auto;
top: 20px;
border-radius: 50px;
padding: 20px 0px;
left: 50%;
z-index: 10;
transform: translateX(-50%);
}
.nav ul {
display: flex;
justify-content: center;
gap: 50px;
}
.nav ul li {
list-style: none;
transform: none;
transition: all 0.1s ease-in-out;
}
.nav ul li:hover {
transform: scale(1.05);
}

View File

@@ -0,0 +1,20 @@
import type { Config } from "tailwindcss";
const config: Config = {
content: [
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}",
"./app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
backgroundImage: {
"gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
"gradient-conic":
"conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
},
},
},
plugins: [],
};
export default config;

View File

@@ -0,0 +1,26 @@
{
"compilerOptions": {
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}