From 342af0c71b8464b10518f2fe14355fa038e2db4e Mon Sep 17 00:00:00 2001 From: gofnnp Date: Mon, 16 Jun 2025 12:07:30 +0400 Subject: [PATCH] main add api --- .prettierignore | 8 ++ .prettierrc.json | 7 ++ next.config.ts | 13 ++- package-lock.json | 17 ++++ package.json | 4 +- src/app/(core)/error.module.scss | 6 ++ src/app/(core)/error.tsx | 32 +++++++ src/app/(core)/loading.module.scss | 5 ++ src/app/(core)/loading.tsx | 10 +++ src/app/(core)/page.tsx | 40 ++++++--- .../AdviserCard/AdviserCard.module.scss | 8 +- src/components/AdviserCard/AdviserCard.tsx | 40 ++++----- .../AdvisersSection.module.scss | 4 + .../AdvisersSection/AdvisersSection.tsx | 34 ++++---- .../CompatibilityCard.module.scss | 5 ++ .../CompatibilityCard/CompatibilityCard.tsx | 22 +++-- .../CompatibilitySection.module.scss | 4 + .../CompatibilitySection.tsx | 34 ++++---- .../MeditationCard/MeditationCard.module.scss | 5 ++ .../MeditationCard/MeditationCard.tsx | 20 +++-- .../MeditationSection.module.scss | 4 + .../MeditationSection/MeditationSection.tsx | 29 ++++--- src/components/PalmCard/PalmCard.module.scss | 5 ++ src/components/PalmCard/PalmCard.tsx | 20 +++-- .../PalmSection/PalmSection.module.scss | 4 + src/components/PalmSection/PalmSection.tsx | 29 ++++--- src/components/ui/Icon/Icon.tsx | 16 +++- src/components/ui/Icon/icons/Star.tsx | 8 ++ .../ui/Skeleton/Skeleton.module.scss | 26 ++++++ src/components/ui/Skeleton/Skeleton.tsx | 12 +++ src/components/ui/Spinner/Spinner.tsx | 33 ++++++++ src/components/ui/Spinner/styles.module.scss | 19 +++++ src/components/ui/Stars/Stars.module.scss | 17 ++++ src/components/ui/Stars/Stars.tsx | 48 +++++++++++ src/entities/dashboard/api.ts | 12 +++ src/entities/dashboard/loaders.ts | 17 ++++ src/entities/dashboard/types.ts | 83 +++++++++++++++++++ src/shared/api/httpClient.ts | 61 ++++++++++++++ src/shared/utils/delay.ts | 2 + 39 files changed, 644 insertions(+), 119 deletions(-) create mode 100644 .prettierignore create mode 100644 .prettierrc.json create mode 100644 src/app/(core)/error.module.scss create mode 100644 src/app/(core)/error.tsx create mode 100644 src/app/(core)/loading.module.scss create mode 100644 src/app/(core)/loading.tsx create mode 100644 src/components/ui/Icon/icons/Star.tsx create mode 100644 src/components/ui/Skeleton/Skeleton.module.scss create mode 100644 src/components/ui/Skeleton/Skeleton.tsx create mode 100644 src/components/ui/Spinner/Spinner.tsx create mode 100644 src/components/ui/Spinner/styles.module.scss create mode 100644 src/components/ui/Stars/Stars.module.scss create mode 100644 src/components/ui/Stars/Stars.tsx create mode 100644 src/entities/dashboard/api.ts create mode 100644 src/entities/dashboard/loaders.ts create mode 100644 src/entities/dashboard/types.ts create mode 100644 src/shared/api/httpClient.ts create mode 100644 src/shared/utils/delay.ts diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..72fee2c --- /dev/null +++ b/.prettierignore @@ -0,0 +1,8 @@ +# Ignore artifacts: +build +.next/ +dist +node_modules/ + +# Ignore files: +package-lock.json \ No newline at end of file diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..4845cc5 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,7 @@ +{ + "semi": true, + "tabWidth": 2, + "printWidth": 100, + "singleQuote": true, + "trailingComma": "es5" +} \ No newline at end of file diff --git a/next.config.ts b/next.config.ts index e9ffa30..b9bd58d 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,7 +1,18 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { - /* config options here */ + env: { + NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL, + }, + images: { + remotePatterns: [ + { + protocol: "https", + hostname: "aura-node.s3.eu-west-2.amazonaws.com", + pathname: "/**", + }, + ], + }, }; export default nextConfig; diff --git a/package-lock.json b/package-lock.json index e218724..0ba87e1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "@types/react-dom": "^19", "eslint": "^9", "eslint-config-next": "15.3.3", + "prettier": "^3.5.3", "typescript": "^5" } }, @@ -4626,6 +4627,22 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", diff --git a/package.json b/package.json index b5bcb52..685c71c 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,8 @@ "next": "15.3.3", "react": "^19.0.0", "react-dom": "^19.0.0", - "sass": "^1.89.2" + "sass": "^1.89.2", + "zod": "^3.25.64" }, "devDependencies": { "@eslint/eslintrc": "^3", @@ -22,6 +23,7 @@ "@types/react-dom": "^19", "eslint": "^9", "eslint-config-next": "15.3.3", + "prettier": "^3.5.3", "typescript": "^5" } } diff --git a/src/app/(core)/error.module.scss b/src/app/(core)/error.module.scss new file mode 100644 index 0000000..0351b6d --- /dev/null +++ b/src/app/(core)/error.module.scss @@ -0,0 +1,6 @@ +.coreError { + display: flex; + flex-direction: column; + align-items: center; + gap: 16px; +} \ No newline at end of file diff --git a/src/app/(core)/error.tsx b/src/app/(core)/error.tsx new file mode 100644 index 0000000..22f0b06 --- /dev/null +++ b/src/app/(core)/error.tsx @@ -0,0 +1,32 @@ +'use client'; + +import Button from '@/components/ui/Button/Button'; +import Typography from '@/components/ui/Typography/Typography'; +import { useEffect } from 'react'; +import styles from "./error.module.scss" + +export default function Error({ + error, + reset, +}: { + error: Error & { digest?: string }; + reset: () => void; +}) { + useEffect(() => { + console.error(error); + }, [error]); + + return ( +
+ Something went wrong! + {error.message} + +
+ ); +} \ No newline at end of file diff --git a/src/app/(core)/loading.module.scss b/src/app/(core)/loading.module.scss new file mode 100644 index 0000000..6c0ba2e --- /dev/null +++ b/src/app/(core)/loading.module.scss @@ -0,0 +1,5 @@ +.coreSpinnerContainer { + width: 100%; + display: flex; + justify-content: center; +} \ No newline at end of file diff --git a/src/app/(core)/loading.tsx b/src/app/(core)/loading.tsx new file mode 100644 index 0000000..902aa8f --- /dev/null +++ b/src/app/(core)/loading.tsx @@ -0,0 +1,10 @@ +import Spinner from "@/components/ui/Spinner/Spinner"; +import styles from "./loading.module.scss" + +export default function Loading() { + return ( +
+ +
+ ); +} \ No newline at end of file diff --git a/src/app/(core)/page.tsx b/src/app/(core)/page.tsx index ce53bf6..7b3cb88 100644 --- a/src/app/(core)/page.tsx +++ b/src/app/(core)/page.tsx @@ -1,16 +1,32 @@ +import { Suspense } from "react"; import Horoscope from "@/components/Horoscope/Horoscope"; -import styles from "./page.module.scss" -import AdvisersSection from "@/components/AdvisersSection/AdvisersSection"; -import CompatibilitySection from "@/components/CompatibilitySection/CompatibilitySection"; -import MeditationSection from "@/components/MeditationSection/MeditationSection"; -import PalmSection from "@/components/PalmSection/PalmSection"; +import styles from "./page.module.scss"; +import AdvisersSection, { AdvisersSectionSkeleton } from "@/components/AdvisersSection/AdvisersSection"; +import CompatibilitySection, { CompatibilitySectionSkeleton } from "@/components/CompatibilitySection/CompatibilitySection"; +import MeditationSection, { MeditationSectionSkeleton } from "@/components/MeditationSection/MeditationSection"; +import PalmSection, { PalmSectionSkeleton } from "@/components/PalmSection/PalmSection"; +import { loadAssistants, loadCompatibility, loadMeditations, loadPalms } from "@/entities/dashboard/loaders"; export default function Home() { - return
- - - - - -
; + return ( +
+ + + }> + + + + }> + + + + }> + + + + }> + + +
+ ); } \ No newline at end of file diff --git a/src/components/AdviserCard/AdviserCard.module.scss b/src/components/AdviserCard/AdviserCard.module.scss index 638be48..d2dba98 100644 --- a/src/components/AdviserCard/AdviserCard.module.scss +++ b/src/components/AdviserCard/AdviserCard.module.scss @@ -8,6 +8,8 @@ position: relative; overflow: hidden; background-repeat: no-repeat; + background-size: cover; + background-position: center; } .content { @@ -46,12 +48,6 @@ display: flex; align-items: center; gap: 4px; - - &>.stars { - display: flex; - align-items: center; - gap: 3px; - } } } } diff --git a/src/components/AdviserCard/AdviserCard.tsx b/src/components/AdviserCard/AdviserCard.tsx index 0aa3ae1..0b03367 100644 --- a/src/components/AdviserCard/AdviserCard.tsx +++ b/src/components/AdviserCard/AdviserCard.tsx @@ -3,44 +3,38 @@ import Button from "../ui/Button/Button" import Card from "../ui/Card/Card" import Typography from "../ui/Typography/Typography" import styles from "./AdviserCard.module.scss" +import { Assistant } from "@/entities/dashboard/types" +import Stars from "../ui/Stars/Stars" -export default function AdviserCard() { +type AdviserCardProps = Assistant; + +export default function AdviserCard({ + name, + photoUrl, + rating, + reviewCount, + description +}: AdviserCardProps) { return ( - +
- Marta + {name}
- Astrologer - 7 years + {description}
- 4.8 + {rating} -
- - - - - - - - - - - - - - - -
+ - (5762) + ({reviewCount})
diff --git a/src/components/AdvisersSection/AdvisersSection.module.scss b/src/components/AdvisersSection/AdvisersSection.module.scss index 3b4a558..3b7415d 100644 --- a/src/components/AdvisersSection/AdvisersSection.module.scss +++ b/src/components/AdvisersSection/AdvisersSection.module.scss @@ -9,4 +9,8 @@ .grid { padding-right: 16px; +} + +.advisersSkeleton.skeleton { + height: 486px; } \ No newline at end of file diff --git a/src/components/AdvisersSection/AdvisersSection.tsx b/src/components/AdvisersSection/AdvisersSection.tsx index 321778c..f5f2a0e 100644 --- a/src/components/AdvisersSection/AdvisersSection.tsx +++ b/src/components/AdvisersSection/AdvisersSection.tsx @@ -1,29 +1,31 @@ +import { Assistant } from "@/entities/dashboard/types"; import AdviserCard from "../AdviserCard/AdviserCard"; import Grid from "../ui/Grid/Grid" import Section from "../ui/Section/Section" import styles from "./AdvisersSection.module.scss" +import { use } from "react"; +import Skeleton from "../ui/Skeleton/Skeleton"; +import clsx from "clsx"; -const advisers = [ - { name: "Marta", img: "/marta.jpg", rating: 4.8, years: 7 }, - { name: "Marta", img: "/marta.jpg", rating: 4.8, years: 7 }, - { name: "Marta", img: "/marta.jpg", rating: 4.8, years: 7 }, - { name: "Marta", img: "/marta.jpg", rating: 4.8, years: 7 }, - { name: "Marta", img: "/marta.jpg", rating: 4.8, years: 7 }, - { name: "Marta", img: "/marta.jpg", rating: 4.8, years: 7 }, - { name: "Marta", img: "/marta.jpg", rating: 4.8, years: 7 }, - { name: "Marta", img: "/marta.jpg", rating: 4.8, years: 7 }, - { name: "Marta", img: "/marta.jpg", rating: 4.8, years: 7 }, - { name: "Marta", img: "/marta.jpg", rating: 4.8, years: 7 }, -]; +export default function AdvisersSection({ promise }: { promise: Promise }) { + const assistants = use(promise); + const columns = Math.ceil(assistants?.length / 2); -export default function AdvisersSection() { return (
- - {advisers.map((adviser, index) => ( - + + {assistants.map((adviser) => ( + ))}
) +} + +export function AdvisersSectionSkeleton() { + return ( +
+ +
+ ) } \ No newline at end of file diff --git a/src/components/CompatibilityCard/CompatibilityCard.module.scss b/src/components/CompatibilityCard/CompatibilityCard.module.scss index 5cf1a49..925e259 100644 --- a/src/components/CompatibilityCard/CompatibilityCard.module.scss +++ b/src/components/CompatibilityCard/CompatibilityCard.module.scss @@ -13,4 +13,9 @@ display: flex; flex-direction: column; justify-content: space-between; +} + +.compatibilityImage { + object-fit: cover; + object-position: center; } \ No newline at end of file diff --git a/src/components/CompatibilityCard/CompatibilityCard.tsx b/src/components/CompatibilityCard/CompatibilityCard.tsx index 8abdfec..1144797 100644 --- a/src/components/CompatibilityCard/CompatibilityCard.tsx +++ b/src/components/CompatibilityCard/CompatibilityCard.tsx @@ -4,28 +4,36 @@ import Typography from "../ui/Typography/Typography" import styles from "./CompatibilityCard.module.scss" import { IconName } from "../ui/Icon/Icon"; import MetaLabel from "../ui/MetaLabel/MetaLabel"; +import { CompatibilityAction } from "@/entities/dashboard/types"; -export default function CompatibilityCard() { +type CompatibilityCardProps = CompatibilityAction; + +export default function CompatibilityCard({ + imageUrl, + title, + type, + minutes +}: CompatibilityCardProps) { return ( Compatibility image
- - Compatibility + + {title} Article + children: {type} }}> - 5 min + {minutes} min
diff --git a/src/components/CompatibilitySection/CompatibilitySection.module.scss b/src/components/CompatibilitySection/CompatibilitySection.module.scss index 15f758e..8d37ec7 100644 --- a/src/components/CompatibilitySection/CompatibilitySection.module.scss +++ b/src/components/CompatibilitySection/CompatibilitySection.module.scss @@ -7,4 +7,8 @@ .grid { padding-right: 16px; +} + +.compatibilitySkeleton.skeleton { + height: 236px; } \ No newline at end of file diff --git a/src/components/CompatibilitySection/CompatibilitySection.tsx b/src/components/CompatibilitySection/CompatibilitySection.tsx index 64b57fa..82ecdfc 100644 --- a/src/components/CompatibilitySection/CompatibilitySection.tsx +++ b/src/components/CompatibilitySection/CompatibilitySection.tsx @@ -1,29 +1,31 @@ +import { CompatibilityAction } from "@/entities/dashboard/types"; import CompatibilityCard from "../CompatibilityCard/CompatibilityCard"; import Grid from "../ui/Grid/Grid" import Section from "../ui/Section/Section" import styles from "./CompatibilitySection.module.scss" +import { use } from "react"; +import Skeleton from "../ui/Skeleton/Skeleton"; +import clsx from "clsx"; -const compatibilities = [ - { title: "Compatibility" }, - { title: "Compatibility" }, - { title: "Compatibility" }, - { title: "Compatibility" }, - { title: "Compatibility" }, - { title: "Compatibility" }, - { title: "Compatibility" }, - { title: "Compatibility" }, - { title: "Compatibility" }, - { title: "Compatibility" }, -]; +export default function CompatibilitySection({ promise }: { promise: Promise }) { + const compatibilities = use(promise); + const columns = Math.ceil(compatibilities?.length / 2); -export default function CompatibilitySection() { return (
- - {compatibilities.map((compatibility, index) => ( - + + {compatibilities.map((compatibility) => ( + ))}
) +} + +export function CompatibilitySectionSkeleton() { + return ( +
+ +
+ ) } \ No newline at end of file diff --git a/src/components/MeditationCard/MeditationCard.module.scss b/src/components/MeditationCard/MeditationCard.module.scss index 15d5d07..82a7838 100644 --- a/src/components/MeditationCard/MeditationCard.module.scss +++ b/src/components/MeditationCard/MeditationCard.module.scss @@ -33,4 +33,9 @@ transform: rotate(180deg); } } +} + +.meditationImage { + object-fit: cover; + object-position: center; } \ No newline at end of file diff --git a/src/components/MeditationCard/MeditationCard.tsx b/src/components/MeditationCard/MeditationCard.tsx index 6edbf2a..8db2cc5 100644 --- a/src/components/MeditationCard/MeditationCard.tsx +++ b/src/components/MeditationCard/MeditationCard.tsx @@ -5,13 +5,21 @@ import styles from "./MeditationCard.module.scss" import Icon, { IconName } from "../ui/Icon/Icon"; import MetaLabel from "../ui/MetaLabel/MetaLabel"; import Button from "../ui/Button/Button"; +import { Meditation } from "@/entities/dashboard/types"; -export default function MeditationCard() { +type MeditationCardProps = Meditation; + +export default function MeditationCard({ + imageUrl, + title, + type, + minutes +}: MeditationCardProps) { return ( Meditation image
- Reset + {title} Therapy + children: {type} }}> - 15 min + {minutes} min