Commit 25bed705 authored by Ли Джен Сеп's avatar Ли Джен Сеп 💬

Initial commit

parents
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# env files (can opt-in for committing if needed)
.env*
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/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/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
## 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/app/building-your-application/deploying) for more details.
import { dirname } from "path";
import { fileURLToPath } from "url";
import { FlatCompat } from "@eslint/eslintrc";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const compat = new FlatCompat({
baseDirectory: __dirname,
});
const eslintConfig = [
...compat.extends("next/core-web-vitals", "next/typescript"),
];
export default eslintConfig;
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
images: {
remotePatterns: [
{
protocol: "https",
hostname: "flagcdn.com",
},
],
},
};
export default nextConfig;
This diff is collapsed.
{
"name": "less1",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev --turbopack",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"react": "^19.0.0",
"react-dom": "^19.0.0",
"next": "15.3.3"
},
"devDependencies": {
"typescript": "^5",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"eslint": "^9",
"eslint-config-next": "15.3.3",
"@eslint/eslintrc": "^3"
}
}
* {
box-sizing: border-box;
padding: 0;
margin: 0;
}
body {
background-color: #fff;
color: #000;
}
@media (prefers-color-scheme: dark) {
html {
color-scheme: dark;
}
}
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}
import { HomePage } from "@/components/pages/Home";
import { baseUrl } from "@/helpers/api";
export default async function Home() {
const res = await fetch(baseUrl + "/all");
const data = await res.json();
console.log(data);
return <HomePage countryList={data} />;
}
"use client";
import { TCountry } from "@/types/type";
import { CountriesList } from "../ui/CountriesList/CountriesList";
import { useState } from "react";
import { CountriesInfo } from "../ui/CountriesInfo/CountriesInfo";
import { baseUrl } from "@/helpers/api";
type Props = {
countryList: TCountry[];
};
export const HomePage = ({ countryList }: Props) => {
const [country, setCountry] = useState<TCountry | null>(null);
const [bCountries, setBCountries] = useState<TCountry[]>([]);
const handleSelectCountry = (cca2: string) => {
const newCountry = countryList.find((c) => c.cca2 === cca2);
if (!newCountry) {
console.log("Нет такой страны");
return;
}
setCountry(newCountry);
if (!newCountry.borders) {
setBCountries([]);
return;
}
fetchBorderedCountry(newCountry.borders);
};
const fetchBorderedCountry = async (arr: string[]) => {
const responses = await Promise.all(
arr.map((code) => fetch(`${baseUrl}/alpha/${code}`).then((res) => res.json())),
);
setBCountries(responses.flat());
};
return (
<div>
<CountriesList countryList={countryList} onSelect={handleSelectCountry} />
<CountriesInfo country={country} borderCountries={bCountries} />
</div>
);
};
import { TCountry } from "@/types/type";
import Image from "next/image";
type Props = {
country: TCountry | null;
borderCountries: TCountry[];
};
export const CountriesInfo = ({ country, borderCountries }: Props) => {
if (!country) return <div>Choose country</div>;
const flagUrl = country.flags.png || "/placeholder.png";
const flagAlt = country.flags.alt || country.name.common;
return (
<div>
<div>
<h2>Country: {country.name.common}</h2>
<Image src={flagUrl} alt={flagAlt} width={300} height={150} />
</div>
<ul>
<li>capital: {country.capital}</li>
<li>area: {country.area}</li>
<li>region: {country.region}</li>
</ul>
<ul>
{borderCountries.map((c, index) => (
<li key={index}>{c.name.common}</li>
))}
</ul>
</div>
);
};
.list {
overflow-y: scroll;
height: 600px;
border: 1px solid #000;
padding: 15px;
width: 300px;
list-style: none;
}
.list > li {
cursor: pointer;
margin: 5px 0;
}
import { TCountry } from "@/types/type";
import styles from "./CountriesList.module.css";
type Props = {
countryList: TCountry[];
onSelect: (cca2: string) => void;
};
export const CountriesList = ({ countryList, onSelect }: Props) => {
return (
<div>
<ul className={styles.list}>
{countryList.map((c, index) => (
<li key={index} onClick={() => onSelect(c.cca2)}>
{c.name.common}
</li>
))}
</ul>
</div>
);
};
export const baseUrl = "https://restcountries.com/v3.1";
export type TCountry = {
name: { common: string };
cca2: string;
capital: string[];
borders: string[];
area: number;
region: string;
flags: {
png: string;
alt: string;
};
};
{
"compilerOptions": {
"target": "ES2017",
"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": {
"@/*": ["./src/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# env files (can opt-in for committing if needed)
.env*
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/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/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
## 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/app/building-your-application/deploying) for more details.
import { dirname } from "path";
import { fileURLToPath } from "url";
import { FlatCompat } from "@eslint/eslintrc";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const compat = new FlatCompat({
baseDirectory: __dirname,
});
const eslintConfig = [
...compat.extends("next/core-web-vitals", "next/typescript"),
];
export default eslintConfig;
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
images: {
remotePatterns: [
{
protocol: "https",
hostname: "flagcdn.com",
},
],
},
};
export default nextConfig;
This diff is collapsed.
{
"name": "les",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev --turbopack",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"axios": "^1.9.0",
"next": "15.3.3",
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"devDependencies": {
"@eslint/eslintrc": "^3",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"eslint": "^9",
"eslint-config-next": "15.3.3",
"typescript": "^5"
}
}
* {
box-sizing: border-box;
padding: 0;
margin: 0;
}
body {
background-color: white;
color: black;
}
@media (prefers-color-scheme: dark) {
html {
color-scheme: dark;
}
}
import "./globals.css";
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}
import { Homepage } from "@/components/pages/Homepage/Homepage";
import { TCountry } from "@/types/types";
export default async function Home({ searchParams }: { searchParams: Promise<{ country?: string }> }) {
//С 15 версии некста квери параметры стали промисами, так что их нужно await-тить
const { country } = await searchParams;
// Почему то api стран перестало возвращать страны со всеми данными так что тут беру только нужные поля
const res = await fetch("https://restcountries.com/v3.1/all?fields=name,cca2,borders,flags");
const countriesList = await res.json();
const selectedCountry = country ? countriesList.find((c: TCountry) => c.cca2 === country) : null;
const borderedCountries =
selectedCountry?.borders?.map((code: string) => countriesList.find((c: TCountry) => c.cca2 === code)) ?? [];
return (
<Homepage
countriesList={countriesList}
selectedCountry={selectedCountry}
borderedCountries={borderedCountries}
/>
);
}
.homeWrapper {
display: flex;
justify-content: space-between;
width: 1200px;
margin: 7vw auto;
padding: 15px;
border: 5px solid #444343;
border-radius: 15px;
}
"use client";
import { useRouter, useSearchParams } from "next/navigation";
import { CountryInfo } from "@/components/ui/CountryInfo/CountryInfo";
import { CountryList } from "@/components/ui/CountryList/CountryList";
import type { TCountry } from "@/types/types";
import styles from "./Homepage.module.css";
type Props = {
countriesList: TCountry[];
selectedCountry: TCountry | null;
borderedCountries: TCountry[];
};
export const Homepage = ({ countriesList, selectedCountry, borderedCountries }: Props) => {
const router = useRouter();
const searchParams = useSearchParams();
const handleChooseCountry = (cca2: string) => {
// Тут создается объект квери параметров
const params = new URLSearchParams(searchParams.toString());
// Тут в этот объект устанавливаем country. В итоге получится country=cca2
params.set("country", cca2);
router.push("?" + params.toString());
};
return (
<div className={styles.homeWrapper}>
<CountryList countryList={countriesList} onChange={handleChooseCountry} />
<CountryInfo country={selectedCountry} borders={borderedCountries} />
</div>
);
};
.preloader {
width: 70%;
display: flex;
justify-content: center;
align-items: center;
border-radius: 15px;
border: 1px solid #000;
}
.wrapper {
width: 70%;
display: flex;
flex-direction: column;
padding: 15px;
border-radius: 15px;
border: 1px solid #000;
}
.wrapper > ul {
list-style: none;
text-transform: capitalize;
margin: 10px 0;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
}
.header > img {
border: 1px solid #000;
}
import { TCountry } from "@/types/types";
import Image from "next/image";
import styles from "./CountryInfo.module.css";
type Props = {
country: TCountry | null;
borders: TCountry[];
};
export const CountryInfo = ({ country, borders }: Props) => {
if (!country) return <div className={styles.preloader}>ChooseCountry</div>;
const imageUrl = country.flags.png || "/defaultFlag.pnp";
const imageAlt = country.flags.alt || country.name.common;
return (
<div className={styles.wrapper}>
<div className={styles.header}>
<h2>Country: {country.name.common}</h2>
<Image src={imageUrl} alt={imageAlt} width={300} height={150} />
</div>
<ul>
<li>
<b>capital:</b> {country.capital}
</li>
<li>
<b>area:</b> {country.area}
</li>
<li>
<b>population:</b> {country.population}
</li>
</ul>
<ul>
<li>
<b>Neighbor:</b>
</li>
{borders.filter(Boolean).map((borderCountry, index) => (
<li key={index}>{borderCountry?.name?.common}</li>
))}
</ul>
</div>
);
};
.countryList {
overflow-y: scroll;
height: 600px;
width: 300px;
border-radius: 15px 0 0 15px;
padding: 15px;
border: 1px solid #000;
}
.countryUl {
list-style: none;
}
.countryUl > li {
margin: 5px 0;
cursor: pointer;
}
.countryUl > li:hover {
background-color: #d1cfcf;
}
import { TCountry } from "@/types/types";
import styles from "./CountryList.module.css";
type Props = {
countryList: TCountry[];
onChange: (cca2: string) => void;
};
export const CountryList = ({ countryList, onChange }: Props) => {
return (
<div className={styles.countryList}>
<ul className={styles.countryUl}>
{countryList.map((country, index) => (
<li key={index} onClick={() => onChange(country.cca2)}>
{country.name.common}
</li>
))}
</ul>
</div>
);
};
export const baseUrl = "https://restcountries.com/v3.1";
export type TCountry = {
name: {
common: string;
};
capital: string[];
borders: string[];
cca2: string;
area: number;
population: number;
flags: {
png: string;
alt: string;
};
};
{
"compilerOptions": {
"target": "ES2017",
"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": {
"@/*": ["./src/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment